@atlaskit/page-layout 1.6.1 → 1.6.3

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 (55) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/cjs/common/hooks/use-is-sidebar-dragging.js +3 -0
  3. package/dist/cjs/components/resize-control/index.js +83 -28
  4. package/dist/cjs/components/slots/main.js +0 -1
  5. package/dist/cjs/version.json +1 -1
  6. package/dist/es2019/common/hooks/use-is-sidebar-dragging.js +3 -0
  7. package/dist/es2019/components/resize-control/index.js +73 -23
  8. package/dist/es2019/components/slots/main.js +0 -1
  9. package/dist/es2019/version.json +1 -1
  10. package/dist/esm/common/hooks/use-is-sidebar-dragging.js +3 -0
  11. package/dist/esm/components/resize-control/index.js +85 -30
  12. package/dist/esm/components/slots/main.js +0 -1
  13. package/dist/esm/version.json +1 -1
  14. package/dist/types-ts4.5/common/constants.d.ts +47 -0
  15. package/dist/types-ts4.5/common/hooks/index.d.ts +2 -0
  16. package/dist/types-ts4.5/common/hooks/use-is-sidebar-collapsing.d.ts +2 -0
  17. package/dist/types-ts4.5/common/hooks/use-is-sidebar-dragging.d.ts +2 -0
  18. package/dist/types-ts4.5/common/safe-local-storage.d.ts +2 -0
  19. package/dist/types-ts4.5/common/types.d.ts +117 -0
  20. package/dist/types-ts4.5/common/utils.d.ts +13 -0
  21. package/dist/types-ts4.5/components/index.d.ts +12 -0
  22. package/dist/types-ts4.5/components/resize-control/grab-area.d.ts +9 -0
  23. package/dist/types-ts4.5/components/resize-control/index.d.ts +4 -0
  24. package/dist/types-ts4.5/components/resize-control/resize-button.d.ts +4 -0
  25. package/dist/types-ts4.5/components/resize-control/shadow.d.ts +6 -0
  26. package/dist/types-ts4.5/components/resize-control/types.d.ts +25 -0
  27. package/dist/types-ts4.5/components/skip-links/index.d.ts +2 -0
  28. package/dist/types-ts4.5/components/skip-links/skip-link-components.d.ts +18 -0
  29. package/dist/types-ts4.5/components/skip-links/types.d.ts +8 -0
  30. package/dist/types-ts4.5/components/skip-links/use-custom-skip-link.d.ts +2 -0
  31. package/dist/types-ts4.5/components/slots/banner-slot.d.ts +12 -0
  32. package/dist/types-ts4.5/components/slots/content.d.ts +23 -0
  33. package/dist/types-ts4.5/components/slots/internal/left-sidebar-inner.d.ts +10 -0
  34. package/dist/types-ts4.5/components/slots/internal/left-sidebar-outer.d.ts +13 -0
  35. package/dist/types-ts4.5/components/slots/internal/resizable-children-wrapper.d.ts +11 -0
  36. package/dist/types-ts4.5/components/slots/internal/slot-focus-ring.d.ts +20 -0
  37. package/dist/types-ts4.5/components/slots/left-panel.d.ts +12 -0
  38. package/dist/types-ts4.5/components/slots/left-sidebar-without-resize.d.ts +12 -0
  39. package/dist/types-ts4.5/components/slots/left-sidebar.d.ts +12 -0
  40. package/dist/types-ts4.5/components/slots/main.d.ts +12 -0
  41. package/dist/types-ts4.5/components/slots/page-layout.d.ts +12 -0
  42. package/dist/types-ts4.5/components/slots/right-panel.d.ts +12 -0
  43. package/dist/types-ts4.5/components/slots/right-sidebar.d.ts +12 -0
  44. package/dist/types-ts4.5/components/slots/slot-dimensions.d.ts +7 -0
  45. package/dist/types-ts4.5/components/slots/top-navigation.d.ts +12 -0
  46. package/dist/types-ts4.5/controllers/index.d.ts +6 -0
  47. package/dist/types-ts4.5/controllers/sidebar-resize-context.d.ts +42 -0
  48. package/dist/types-ts4.5/controllers/sidebar-resize-controller.d.ts +3 -0
  49. package/dist/types-ts4.5/controllers/skip-link-context.d.ts +5 -0
  50. package/dist/types-ts4.5/controllers/skip-link-controller.d.ts +4 -0
  51. package/dist/types-ts4.5/controllers/types.d.ts +22 -0
  52. package/dist/types-ts4.5/controllers/use-page-layout-grid.d.ts +3 -0
  53. package/dist/types-ts4.5/controllers/use-update-css-vars.d.ts +2 -0
  54. package/dist/types-ts4.5/index.d.ts +4 -0
  55. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @atlaskit/page-layout
2
2
 
3
+ ## 1.6.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`4bbc131de00`](https://bitbucket.org/atlassian/atlassian-frontend/commits/4bbc131de00) - #### Fix: Resizing pages with `<iframe>`s
8
+
9
+ Pages that contain `<iframe>` elements will now have a smoother resizing experience. `<iframe>` elements consume user events (eg `mousemove`) when the user is over the top of them. This is problematic for resizing as we need to have the latest user pointer movements to resize the sidebar. Now, while a resize is happening, `pointer-events` are blocked on `<iframe>` elements to prevent the `<iframe>` consuming user events.
10
+
11
+ #### Fix: User cursor while resizing
12
+
13
+ While resizing the users cursor will now always be `ew-resize`. Previously the cursor could change depending on what element the users pointer was over
14
+
15
+ #### Fix: Resizing will no longer change user selection
16
+
17
+ A user can select parts of a page (eg select a paragraph of text). Previously, in some cases, a user's selection could change due to a resizing operation. This has been fixed so that a resizing operation will no longer change a user's selection
18
+
19
+ #### Fix: `onResizeEnd`
20
+
21
+ `onResizeEnd` will no longer incorrectly get an empty object `{}` if the user resized into the collapsed state
22
+
23
+ ## 1.6.2
24
+
25
+ ### Patch Changes
26
+
27
+ - [`9d00501a414`](https://bitbucket.org/atlassian/atlassian-frontend/commits/9d00501a414) - Ensure legacy types are published for TS 4.5-4.8
28
+
3
29
  ## 1.6.1
4
30
 
5
31
  ### Patch Changes
@@ -15,6 +15,9 @@ var getIsDragging = function getIsDragging() {
15
15
  }
16
16
  return document.documentElement.getAttribute(_constants.IS_SIDEBAR_DRAGGING) === 'true';
17
17
  };
18
+
19
+ // TODO: I think this should be derived from the sidebar state,
20
+ // and not indirectly from observing an attribute change
18
21
  var useIsSidebarDragging = function useIsSidebarDragging() {
19
22
  var _useState = (0, _react.useState)(getIsDragging),
20
23
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
@@ -31,6 +31,33 @@ var resizeControlStyles = (0, _react2.css)({
31
31
  var showResizeButtonStyles = (0, _react2.css)({
32
32
  '--ds--resize-button--opacity': 1
33
33
  });
34
+
35
+ // @ts-expect-error adding `!important` to style rules is currently a type error
36
+ var globalResizingStyles = (0, _react2.css)({
37
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
38
+ '*': {
39
+ // Setting the cursor to be `ew-resize` on all elements so that even if the user
40
+ // pointer slips off the resize handle, the cursor will still be the resize cursor
41
+ cursor: 'ew-resize !important',
42
+ // Blocking selection while resizing
43
+ // Notes:
44
+ // - This prevents a user selection being caused by resizing
45
+ // - Safari + Firefox → all good
46
+ // - Chrome → This will undo the current selection while resizing (not ideal)
47
+ // - The current selection will resume after resizing
48
+ userSelect: 'none !important'
49
+ },
50
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
51
+ iframe: {
52
+ // Disabling pointer events on iframes when resizing
53
+ // as iframes will swallower user events when the user is over them
54
+ pointerEvents: 'none !important'
55
+ }
56
+ // Note: We _could_ also disable `pointer-events` on all elements during resizing.
57
+ // However, to minimize risk we are just disabling `pointer-events` on iframes
58
+ // as that change is actually needed to fix resizing with iframes
59
+ });
60
+
34
61
  var ResizeControl = function ResizeControl(_ref) {
35
62
  var testId = _ref.testId,
36
63
  overrides = _ref.overrides,
@@ -56,6 +83,13 @@ var ResizeControl = function ResizeControl(_ref) {
56
83
  isGrabAreaFocused = _useState2[0],
57
84
  setIsGrabAreaFocused = _useState2[1];
58
85
  var unbindEvents = (0, _react.useRef)(null);
86
+
87
+ // Used in some cases to ensure function references don't have to change
88
+ // TODO: more functions could use `stableSidebarState` rather than `leftSidebarState`
89
+ var stableSidebarState = (0, _react.useRef)(leftSidebarState);
90
+ (0, _react.useEffect)(function () {
91
+ stableSidebarState.current = leftSidebarState;
92
+ }, [leftSidebarState]);
59
93
  var toggleSideBar = function toggleSideBar(e) {
60
94
  if (isResizing) {
61
95
  return;
@@ -90,7 +124,11 @@ var ResizeControl = function ResizeControl(_ref) {
90
124
  offset.current = event.clientX - leftSidebarState[_constants.VAR_LEFT_SIDEBAR_WIDTH] - (0, _utils.getLeftPanelWidth)();
91
125
  unbindEvents.current = (0, _bindEventListener.bindAll)(window, [{
92
126
  type: 'mousemove',
93
- listener: onUpdateResize
127
+ listener: function listener(event) {
128
+ onUpdateResize({
129
+ clientX: event.clientX
130
+ });
131
+ }
94
132
  }, {
95
133
  type: 'mouseup',
96
134
  listener: onFinishResizing
@@ -155,37 +193,45 @@ var ResizeControl = function ResizeControl(_ref) {
155
193
  offset.current = 0;
156
194
  collapseLeftSidebar(undefined, true);
157
195
  };
158
- var onUpdateResize = (0, _rafSchd.default)(function (event) {
159
- // Allow the sidebar to be 50% of the available page width
160
- var maxWidth = Math.round(window.innerWidth / 2);
161
- var leftPanelWidth = (0, _utils.getLeftPanelWidth)();
162
- var leftSidebarWidth = leftSidebarState.leftSidebarWidth;
163
- var hasResizedOffLeftOfScreen = event.clientX < 0;
164
- if (hasResizedOffLeftOfScreen) {
165
- onResizeOffLeftOfScreen();
166
- return;
167
- }
168
- var delta = Math.max(Math.min(event.clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
169
- sidebarWidth.current = Math.max(leftSidebarWidth + delta - offset.current, _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH);
170
- document.documentElement.style.setProperty("--".concat(_constants.VAR_LEFT_SIDEBAR_WIDTH), "".concat(sidebarWidth.current, "px"));
171
- });
172
- var cleanupAfterResize = function cleanupAfterResize() {
173
- var _unbindEvents$current2;
174
- sidebarWidth.current = 0;
175
- offset.current = 0;
176
- (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
177
- unbindEvents.current = null;
178
- };
179
- var updatedLeftSidebarState = {};
196
+
197
+ // It is important that `onUpdateResize` is a stable function reference, so that:
198
+ // 1. we ensure we are correctly throttling with `requestAnimationFrame`
199
+ // 2. that a `onUpdateResize` will cancel the one and only pending frame
200
+ // To help ensure `onUpdateResize` is stable, we are putting the last state into a ref
201
+ var _useState3 = (0, _react.useState)(function () {
202
+ return (0, _rafSchd.default)(function (_ref2) {
203
+ var clientX = _ref2.clientX;
204
+ // Allow the sidebar to be 50% of the available page width
205
+ var maxWidth = Math.round(window.innerWidth / 2);
206
+ var leftPanelWidth = (0, _utils.getLeftPanelWidth)();
207
+ var leftSidebarWidth = stableSidebarState.current.leftSidebarWidth;
208
+ var hasResizedOffLeftOfScreen = clientX < 0;
209
+ if (hasResizedOffLeftOfScreen) {
210
+ onResizeOffLeftOfScreen();
211
+ return;
212
+ }
213
+ var delta = Math.max(Math.min(clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
214
+ sidebarWidth.current = Math.max(leftSidebarWidth + delta - offset.current, _constants.COLLAPSED_LEFT_SIDEBAR_WIDTH);
215
+ document.documentElement.style.setProperty("--".concat(_constants.VAR_LEFT_SIDEBAR_WIDTH), "".concat(sidebarWidth.current, "px"));
216
+ });
217
+ }),
218
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 1),
219
+ onUpdateResize = _useState4[0];
180
220
  var onFinishResizing = function onFinishResizing() {
221
+ var _unbindEvents$current2;
181
222
  if (isLeftSidebarCollapsed) {
182
223
  return;
183
224
  }
184
225
  document.documentElement.removeAttribute(_constants.IS_SIDEBAR_DRAGGING);
185
226
 
227
+ // TODO: the control flow is pretty strange as the first codepath which calls `collapseLeftSidebar()`
228
+ // does not return an updated state snapshot.
229
+ var updatedLeftSidebarState = null;
230
+
186
231
  // If it is dragged to below the threshold,
187
232
  // collapse the navigation
188
233
  if (sidebarWidth.current < _constants.MIN_LEFT_SIDEBAR_DRAG_THRESHOLD) {
234
+ // TODO: for this codepath, `onCollapse` occurs before `onResizeEnd` which seems wrong
189
235
  document.documentElement.style.setProperty("--".concat(_constants.VAR_LEFT_SIDEBAR_WIDTH), "".concat(_constants.COLLAPSED_LEFT_SIDEBAR_WIDTH, "px"));
190
236
  collapseLeftSidebar(undefined, true);
191
237
  }
@@ -207,11 +253,18 @@ var ResizeControl = function ResizeControl(_ref) {
207
253
  }, (0, _defineProperty2.default)(_objectSpread3, _constants.VAR_LEFT_SIDEBAR_WIDTH, sidebarWidth.current), (0, _defineProperty2.default)(_objectSpread3, "lastLeftSidebarWidth", sidebarWidth.current), _objectSpread3));
208
254
  setLeftSidebarState(updatedLeftSidebarState);
209
255
  }
256
+ (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
257
+ unbindEvents.current = null;
258
+ onUpdateResize.cancel();
259
+ sidebarWidth.current = 0;
260
+ offset.current = 0;
261
+
262
+ // TODO: no idea why this is in an animation frame
210
263
  requestAnimationFrame(function () {
211
- onUpdateResize.cancel();
264
+ var _updatedLeftSidebarSt;
212
265
  setIsGrabAreaFocused(false);
213
- onResizeEnd && onResizeEnd(updatedLeftSidebarState);
214
- cleanupAfterResize();
266
+ // Note: the `collapseSidebar` codepath does not return state, so we need to pull it from the ref
267
+ onResizeEnd === null || onResizeEnd === void 0 ? void 0 : onResizeEnd((_updatedLeftSidebarSt = updatedLeftSidebarState) !== null && _updatedLeftSidebarSt !== void 0 ? _updatedLeftSidebarSt : stableSidebarState.current);
215
268
  });
216
269
  };
217
270
  var onKeyDown = function onKeyDown(event) {
@@ -281,7 +334,7 @@ var ResizeControl = function ResizeControl(_ref) {
281
334
  var leftSidebarPercentageExpanded = (0, _utils.getLeftSidebarPercentage)(leftSidebarState.leftSidebarWidth, maxAriaWidth);
282
335
 
283
336
  /* eslint-disable jsx-a11y/role-supports-aria-props */
284
- return (0, _react2.jsx)("div", (0, _extends2.default)({}, cssSelector, {
337
+ return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)("div", (0, _extends2.default)({}, cssSelector, {
285
338
  css: [resizeControlStyles, (isGrabAreaFocused || isLeftSidebarCollapsed) && showResizeButtonStyles]
286
339
  }), (0, _react2.jsx)(_shadow.default, {
287
340
  testId: testId && "".concat(testId, "-shadow")
@@ -304,7 +357,9 @@ var ResizeControl = function ResizeControl(_ref) {
304
357
  label: resizeButtonLabel,
305
358
  onClick: toggleSideBar,
306
359
  testId: testId && "".concat(testId, "-resize-button")
307
- }));
360
+ })), leftSidebarState.isResizing ? (0, _react2.jsx)(_react2.Global, {
361
+ styles: globalResizingStyles
362
+ }) : null);
308
363
  /* eslint-enable jsx-a11y/role-supports-aria-props */
309
364
  };
310
365
 
@@ -28,7 +28,6 @@ var mainStyles = (0, _react2.css)({
28
28
  transition: "margin-left ".concat(_constants.TRANSITION_DURATION, "ms ").concat(_curves.easeOut, " 0s")
29
29
  });
30
30
  var draggingStyles = (0, _react2.css)({
31
- cursor: 'ew-resize',
32
31
  // Make sure drag to resize remains snappy.
33
32
  transition: 'none'
34
33
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "sideEffects": false
5
5
  }
@@ -7,6 +7,9 @@ const getIsDragging = () => {
7
7
  }
8
8
  return document.documentElement.getAttribute(IS_SIDEBAR_DRAGGING) === 'true';
9
9
  };
10
+
11
+ // TODO: I think this should be derived from the sidebar state,
12
+ // and not indirectly from observing an attribute change
10
13
  const useIsSidebarDragging = () => {
11
14
  const [isDragging, setIsDragging] = useState(getIsDragging);
12
15
  useEffect(() => {
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  /** @jsx jsx */
3
- import { useCallback, useContext, useMemo, useRef, useState } from 'react';
4
- import { css, jsx } from '@emotion/react';
3
+ import { Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { css, Global, jsx } from '@emotion/react';
5
5
  import { bindAll } from 'bind-event-listener';
6
6
  import rafSchd from 'raf-schd';
7
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';
@@ -25,6 +25,33 @@ const resizeControlStyles = css({
25
25
  const showResizeButtonStyles = css({
26
26
  '--ds--resize-button--opacity': 1
27
27
  });
28
+
29
+ // @ts-expect-error adding `!important` to style rules is currently a type error
30
+ const globalResizingStyles = css({
31
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
32
+ '*': {
33
+ // Setting the cursor to be `ew-resize` on all elements so that even if the user
34
+ // pointer slips off the resize handle, the cursor will still be the resize cursor
35
+ cursor: 'ew-resize !important',
36
+ // Blocking selection while resizing
37
+ // Notes:
38
+ // - This prevents a user selection being caused by resizing
39
+ // - Safari + Firefox → all good
40
+ // - Chrome → This will undo the current selection while resizing (not ideal)
41
+ // - The current selection will resume after resizing
42
+ userSelect: 'none !important'
43
+ },
44
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
45
+ iframe: {
46
+ // Disabling pointer events on iframes when resizing
47
+ // as iframes will swallower user events when the user is over them
48
+ pointerEvents: 'none !important'
49
+ }
50
+ // Note: We _could_ also disable `pointer-events` on all elements during resizing.
51
+ // However, to minimize risk we are just disabling `pointer-events` on iframes
52
+ // as that change is actually needed to fix resizing with iframes
53
+ });
54
+
28
55
  const ResizeControl = ({
29
56
  testId,
30
57
  overrides,
@@ -49,6 +76,13 @@ const ResizeControl = ({
49
76
  const keyboardEventTimeout = useRef();
50
77
  const [isGrabAreaFocused, setIsGrabAreaFocused] = useState(false);
51
78
  const unbindEvents = useRef(null);
79
+
80
+ // Used in some cases to ensure function references don't have to change
81
+ // TODO: more functions could use `stableSidebarState` rather than `leftSidebarState`
82
+ const stableSidebarState = useRef(leftSidebarState);
83
+ useEffect(() => {
84
+ stableSidebarState.current = leftSidebarState;
85
+ }, [leftSidebarState]);
52
86
  const toggleSideBar = e => {
53
87
  if (isResizing) {
54
88
  return;
@@ -83,7 +117,11 @@ const ResizeControl = ({
83
117
  offset.current = event.clientX - leftSidebarState[VAR_LEFT_SIDEBAR_WIDTH] - getLeftPanelWidth();
84
118
  unbindEvents.current = bindAll(window, [{
85
119
  type: 'mousemove',
86
- listener: onUpdateResize
120
+ listener: function (event) {
121
+ onUpdateResize({
122
+ clientX: event.clientX
123
+ });
124
+ }
87
125
  }, {
88
126
  type: 'mouseup',
89
127
  listener: onFinishResizing
@@ -149,39 +187,42 @@ const ResizeControl = ({
149
187
  offset.current = 0;
150
188
  collapseLeftSidebar(undefined, true);
151
189
  };
152
- const onUpdateResize = rafSchd(event => {
190
+
191
+ // It is important that `onUpdateResize` is a stable function reference, so that:
192
+ // 1. we ensure we are correctly throttling with `requestAnimationFrame`
193
+ // 2. that a `onUpdateResize` will cancel the one and only pending frame
194
+ // To help ensure `onUpdateResize` is stable, we are putting the last state into a ref
195
+ const [onUpdateResize] = useState(() => rafSchd(({
196
+ clientX
197
+ }) => {
153
198
  // Allow the sidebar to be 50% of the available page width
154
199
  const maxWidth = Math.round(window.innerWidth / 2);
155
200
  const leftPanelWidth = getLeftPanelWidth();
156
- const {
157
- leftSidebarWidth
158
- } = leftSidebarState;
159
- const hasResizedOffLeftOfScreen = event.clientX < 0;
201
+ const leftSidebarWidth = stableSidebarState.current.leftSidebarWidth;
202
+ const hasResizedOffLeftOfScreen = clientX < 0;
160
203
  if (hasResizedOffLeftOfScreen) {
161
204
  onResizeOffLeftOfScreen();
162
205
  return;
163
206
  }
164
- const delta = Math.max(Math.min(event.clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
207
+ const delta = Math.max(Math.min(clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
165
208
  sidebarWidth.current = Math.max(leftSidebarWidth + delta - offset.current, COLLAPSED_LEFT_SIDEBAR_WIDTH);
166
209
  document.documentElement.style.setProperty(`--${VAR_LEFT_SIDEBAR_WIDTH}`, `${sidebarWidth.current}px`);
167
- });
168
- const cleanupAfterResize = () => {
169
- var _unbindEvents$current2;
170
- sidebarWidth.current = 0;
171
- offset.current = 0;
172
- (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
173
- unbindEvents.current = null;
174
- };
175
- let updatedLeftSidebarState = {};
210
+ }));
176
211
  const onFinishResizing = () => {
212
+ var _unbindEvents$current2;
177
213
  if (isLeftSidebarCollapsed) {
178
214
  return;
179
215
  }
180
216
  document.documentElement.removeAttribute(IS_SIDEBAR_DRAGGING);
181
217
 
218
+ // TODO: the control flow is pretty strange as the first codepath which calls `collapseLeftSidebar()`
219
+ // does not return an updated state snapshot.
220
+ let updatedLeftSidebarState = null;
221
+
182
222
  // If it is dragged to below the threshold,
183
223
  // collapse the navigation
184
224
  if (sidebarWidth.current < MIN_LEFT_SIDEBAR_DRAG_THRESHOLD) {
225
+ // TODO: for this codepath, `onCollapse` occurs before `onResizeEnd` which seems wrong
185
226
  document.documentElement.style.setProperty(`--${VAR_LEFT_SIDEBAR_WIDTH}`, `${COLLAPSED_LEFT_SIDEBAR_WIDTH}px`);
186
227
  collapseLeftSidebar(undefined, true);
187
228
  }
@@ -207,11 +248,18 @@ const ResizeControl = ({
207
248
  };
208
249
  setLeftSidebarState(updatedLeftSidebarState);
209
250
  }
251
+ (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
252
+ unbindEvents.current = null;
253
+ onUpdateResize.cancel();
254
+ sidebarWidth.current = 0;
255
+ offset.current = 0;
256
+
257
+ // TODO: no idea why this is in an animation frame
210
258
  requestAnimationFrame(() => {
211
- onUpdateResize.cancel();
259
+ var _updatedLeftSidebarSt;
212
260
  setIsGrabAreaFocused(false);
213
- onResizeEnd && onResizeEnd(updatedLeftSidebarState);
214
- cleanupAfterResize();
261
+ // Note: the `collapseSidebar` codepath does not return state, so we need to pull it from the ref
262
+ onResizeEnd === null || onResizeEnd === void 0 ? void 0 : onResizeEnd((_updatedLeftSidebarSt = updatedLeftSidebarState) !== null && _updatedLeftSidebarSt !== void 0 ? _updatedLeftSidebarSt : stableSidebarState.current);
215
263
  });
216
264
  };
217
265
  const onKeyDown = event => {
@@ -287,7 +335,7 @@ const ResizeControl = ({
287
335
  const leftSidebarPercentageExpanded = getLeftSidebarPercentage(leftSidebarState.leftSidebarWidth, maxAriaWidth);
288
336
 
289
337
  /* eslint-disable jsx-a11y/role-supports-aria-props */
290
- return jsx("div", _extends({}, cssSelector, {
338
+ return jsx(Fragment, null, jsx("div", _extends({}, cssSelector, {
291
339
  css: [resizeControlStyles, (isGrabAreaFocused || isLeftSidebarCollapsed) && showResizeButtonStyles]
292
340
  }), jsx(Shadow, {
293
341
  testId: testId && `${testId}-shadow`
@@ -310,7 +358,9 @@ const ResizeControl = ({
310
358
  label: resizeButtonLabel,
311
359
  onClick: toggleSideBar,
312
360
  testId: testId && `${testId}-resize-button`
313
- }));
361
+ })), leftSidebarState.isResizing ? jsx(Global, {
362
+ styles: globalResizingStyles
363
+ }) : null);
314
364
  /* eslint-enable jsx-a11y/role-supports-aria-props */
315
365
  };
316
366
 
@@ -21,7 +21,6 @@ const mainStyles = css({
21
21
  transition: `margin-left ${TRANSITION_DURATION}ms ${easeOut} 0s`
22
22
  });
23
23
  const draggingStyles = css({
24
- cursor: 'ew-resize',
25
24
  // Make sure drag to resize remains snappy.
26
25
  transition: 'none'
27
26
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "sideEffects": false
5
5
  }
@@ -8,6 +8,9 @@ var getIsDragging = function getIsDragging() {
8
8
  }
9
9
  return document.documentElement.getAttribute(IS_SIDEBAR_DRAGGING) === 'true';
10
10
  };
11
+
12
+ // TODO: I think this should be derived from the sidebar state,
13
+ // and not indirectly from observing an attribute change
11
14
  var useIsSidebarDragging = function useIsSidebarDragging() {
12
15
  var _useState = useState(getIsDragging),
13
16
  _useState2 = _slicedToArray(_useState, 2),
@@ -4,8 +4,8 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
5
5
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
6
6
  /** @jsx jsx */
7
- import { useCallback, useContext, useMemo, useRef, useState } from 'react';
8
- import { css, jsx } from '@emotion/react';
7
+ import { Fragment, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
8
+ import { css, Global, jsx } from '@emotion/react';
9
9
  import { bindAll } from 'bind-event-listener';
10
10
  import rafSchd from 'raf-schd';
11
11
  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';
@@ -27,6 +27,33 @@ var resizeControlStyles = css({
27
27
  var showResizeButtonStyles = css({
28
28
  '--ds--resize-button--opacity': 1
29
29
  });
30
+
31
+ // @ts-expect-error adding `!important` to style rules is currently a type error
32
+ var globalResizingStyles = css({
33
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
34
+ '*': {
35
+ // Setting the cursor to be `ew-resize` on all elements so that even if the user
36
+ // pointer slips off the resize handle, the cursor will still be the resize cursor
37
+ cursor: 'ew-resize !important',
38
+ // Blocking selection while resizing
39
+ // Notes:
40
+ // - This prevents a user selection being caused by resizing
41
+ // - Safari + Firefox → all good
42
+ // - Chrome → This will undo the current selection while resizing (not ideal)
43
+ // - The current selection will resume after resizing
44
+ userSelect: 'none !important'
45
+ },
46
+ // eslint-disable-next-line @repo/internal/styles/no-nested-styles
47
+ iframe: {
48
+ // Disabling pointer events on iframes when resizing
49
+ // as iframes will swallower user events when the user is over them
50
+ pointerEvents: 'none !important'
51
+ }
52
+ // Note: We _could_ also disable `pointer-events` on all elements during resizing.
53
+ // However, to minimize risk we are just disabling `pointer-events` on iframes
54
+ // as that change is actually needed to fix resizing with iframes
55
+ });
56
+
30
57
  var ResizeControl = function ResizeControl(_ref) {
31
58
  var testId = _ref.testId,
32
59
  overrides = _ref.overrides,
@@ -52,6 +79,13 @@ var ResizeControl = function ResizeControl(_ref) {
52
79
  isGrabAreaFocused = _useState2[0],
53
80
  setIsGrabAreaFocused = _useState2[1];
54
81
  var unbindEvents = useRef(null);
82
+
83
+ // Used in some cases to ensure function references don't have to change
84
+ // TODO: more functions could use `stableSidebarState` rather than `leftSidebarState`
85
+ var stableSidebarState = useRef(leftSidebarState);
86
+ useEffect(function () {
87
+ stableSidebarState.current = leftSidebarState;
88
+ }, [leftSidebarState]);
55
89
  var toggleSideBar = function toggleSideBar(e) {
56
90
  if (isResizing) {
57
91
  return;
@@ -86,7 +120,11 @@ var ResizeControl = function ResizeControl(_ref) {
86
120
  offset.current = event.clientX - leftSidebarState[VAR_LEFT_SIDEBAR_WIDTH] - getLeftPanelWidth();
87
121
  unbindEvents.current = bindAll(window, [{
88
122
  type: 'mousemove',
89
- listener: onUpdateResize
123
+ listener: function listener(event) {
124
+ onUpdateResize({
125
+ clientX: event.clientX
126
+ });
127
+ }
90
128
  }, {
91
129
  type: 'mouseup',
92
130
  listener: onFinishResizing
@@ -151,37 +189,45 @@ var ResizeControl = function ResizeControl(_ref) {
151
189
  offset.current = 0;
152
190
  collapseLeftSidebar(undefined, true);
153
191
  };
154
- var onUpdateResize = rafSchd(function (event) {
155
- // Allow the sidebar to be 50% of the available page width
156
- var maxWidth = Math.round(window.innerWidth / 2);
157
- var leftPanelWidth = getLeftPanelWidth();
158
- var leftSidebarWidth = leftSidebarState.leftSidebarWidth;
159
- var hasResizedOffLeftOfScreen = event.clientX < 0;
160
- if (hasResizedOffLeftOfScreen) {
161
- onResizeOffLeftOfScreen();
162
- return;
163
- }
164
- var delta = Math.max(Math.min(event.clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
165
- sidebarWidth.current = Math.max(leftSidebarWidth + delta - offset.current, COLLAPSED_LEFT_SIDEBAR_WIDTH);
166
- document.documentElement.style.setProperty("--".concat(VAR_LEFT_SIDEBAR_WIDTH), "".concat(sidebarWidth.current, "px"));
167
- });
168
- var cleanupAfterResize = function cleanupAfterResize() {
169
- var _unbindEvents$current2;
170
- sidebarWidth.current = 0;
171
- offset.current = 0;
172
- (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
173
- unbindEvents.current = null;
174
- };
175
- var updatedLeftSidebarState = {};
192
+
193
+ // It is important that `onUpdateResize` is a stable function reference, so that:
194
+ // 1. we ensure we are correctly throttling with `requestAnimationFrame`
195
+ // 2. that a `onUpdateResize` will cancel the one and only pending frame
196
+ // To help ensure `onUpdateResize` is stable, we are putting the last state into a ref
197
+ var _useState3 = useState(function () {
198
+ return rafSchd(function (_ref2) {
199
+ var clientX = _ref2.clientX;
200
+ // Allow the sidebar to be 50% of the available page width
201
+ var maxWidth = Math.round(window.innerWidth / 2);
202
+ var leftPanelWidth = getLeftPanelWidth();
203
+ var leftSidebarWidth = stableSidebarState.current.leftSidebarWidth;
204
+ var hasResizedOffLeftOfScreen = clientX < 0;
205
+ if (hasResizedOffLeftOfScreen) {
206
+ onResizeOffLeftOfScreen();
207
+ return;
208
+ }
209
+ var delta = Math.max(Math.min(clientX - leftSidebarWidth - leftPanelWidth, maxWidth - leftSidebarWidth - leftPanelWidth), COLLAPSED_LEFT_SIDEBAR_WIDTH - leftSidebarWidth - leftPanelWidth);
210
+ sidebarWidth.current = Math.max(leftSidebarWidth + delta - offset.current, COLLAPSED_LEFT_SIDEBAR_WIDTH);
211
+ document.documentElement.style.setProperty("--".concat(VAR_LEFT_SIDEBAR_WIDTH), "".concat(sidebarWidth.current, "px"));
212
+ });
213
+ }),
214
+ _useState4 = _slicedToArray(_useState3, 1),
215
+ onUpdateResize = _useState4[0];
176
216
  var onFinishResizing = function onFinishResizing() {
217
+ var _unbindEvents$current2;
177
218
  if (isLeftSidebarCollapsed) {
178
219
  return;
179
220
  }
180
221
  document.documentElement.removeAttribute(IS_SIDEBAR_DRAGGING);
181
222
 
223
+ // TODO: the control flow is pretty strange as the first codepath which calls `collapseLeftSidebar()`
224
+ // does not return an updated state snapshot.
225
+ var updatedLeftSidebarState = null;
226
+
182
227
  // If it is dragged to below the threshold,
183
228
  // collapse the navigation
184
229
  if (sidebarWidth.current < MIN_LEFT_SIDEBAR_DRAG_THRESHOLD) {
230
+ // TODO: for this codepath, `onCollapse` occurs before `onResizeEnd` which seems wrong
185
231
  document.documentElement.style.setProperty("--".concat(VAR_LEFT_SIDEBAR_WIDTH), "".concat(COLLAPSED_LEFT_SIDEBAR_WIDTH, "px"));
186
232
  collapseLeftSidebar(undefined, true);
187
233
  }
@@ -203,11 +249,18 @@ var ResizeControl = function ResizeControl(_ref) {
203
249
  }, _defineProperty(_objectSpread3, VAR_LEFT_SIDEBAR_WIDTH, sidebarWidth.current), _defineProperty(_objectSpread3, "lastLeftSidebarWidth", sidebarWidth.current), _objectSpread3));
204
250
  setLeftSidebarState(updatedLeftSidebarState);
205
251
  }
252
+ (_unbindEvents$current2 = unbindEvents.current) === null || _unbindEvents$current2 === void 0 ? void 0 : _unbindEvents$current2.call(unbindEvents);
253
+ unbindEvents.current = null;
254
+ onUpdateResize.cancel();
255
+ sidebarWidth.current = 0;
256
+ offset.current = 0;
257
+
258
+ // TODO: no idea why this is in an animation frame
206
259
  requestAnimationFrame(function () {
207
- onUpdateResize.cancel();
260
+ var _updatedLeftSidebarSt;
208
261
  setIsGrabAreaFocused(false);
209
- onResizeEnd && onResizeEnd(updatedLeftSidebarState);
210
- cleanupAfterResize();
262
+ // Note: the `collapseSidebar` codepath does not return state, so we need to pull it from the ref
263
+ onResizeEnd === null || onResizeEnd === void 0 ? void 0 : onResizeEnd((_updatedLeftSidebarSt = updatedLeftSidebarState) !== null && _updatedLeftSidebarSt !== void 0 ? _updatedLeftSidebarSt : stableSidebarState.current);
211
264
  });
212
265
  };
213
266
  var onKeyDown = function onKeyDown(event) {
@@ -277,7 +330,7 @@ var ResizeControl = function ResizeControl(_ref) {
277
330
  var leftSidebarPercentageExpanded = getLeftSidebarPercentage(leftSidebarState.leftSidebarWidth, maxAriaWidth);
278
331
 
279
332
  /* eslint-disable jsx-a11y/role-supports-aria-props */
280
- return jsx("div", _extends({}, cssSelector, {
333
+ return jsx(Fragment, null, jsx("div", _extends({}, cssSelector, {
281
334
  css: [resizeControlStyles, (isGrabAreaFocused || isLeftSidebarCollapsed) && showResizeButtonStyles]
282
335
  }), jsx(Shadow, {
283
336
  testId: testId && "".concat(testId, "-shadow")
@@ -300,7 +353,9 @@ var ResizeControl = function ResizeControl(_ref) {
300
353
  label: resizeButtonLabel,
301
354
  onClick: toggleSideBar,
302
355
  testId: testId && "".concat(testId, "-resize-button")
303
- }));
356
+ })), leftSidebarState.isResizing ? jsx(Global, {
357
+ styles: globalResizingStyles
358
+ }) : null);
304
359
  /* eslint-enable jsx-a11y/role-supports-aria-props */
305
360
  };
306
361
 
@@ -21,7 +21,6 @@ var mainStyles = css({
21
21
  transition: "margin-left ".concat(TRANSITION_DURATION, "ms ").concat(easeOut, " 0s")
22
22
  });
23
23
  var draggingStyles = css({
24
- cursor: 'ew-resize',
25
24
  // Make sure drag to resize remains snappy.
26
25
  transition: 'none'
27
26
  });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "sideEffects": false
5
5
  }