@atlaskit/page-layout 1.4.0 → 1.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @atlaskit/page-layout
2
2
 
3
+ ## 1.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`56507598609`](https://bitbucket.org/atlassian/atlassian-frontend/commits/56507598609) - Skip minor dependency bump
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+
13
+ ## 1.5.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [`2a9f6f800ef`](https://bitbucket.org/atlassian/atlassian-frontend/commits/2a9f6f800ef) - **Fixes**
18
+
19
+ - `onLeftSidebarExpand` is no longer called when the sidebar is already open. `onLeftSidebarExpand` oculd previously be incorrectly called if a user resized an expanded sidebar to slightly smaller than the default sidebar width, or when the user cancelled a sidebar resizing operation with the `"Escape"` key
20
+ - the latest provided `onLeftSidebarCollapse` and `onLeftSidebarExpand` functions are now called when collapsing / expanding respectively. Previously, only the initial `onLeftSidebarCollapse` and `onLeftSidebarExpand` were called (due to a stale closure)
21
+ - `onLeftSidebarCollapse` and `onLeftSidebarExpand` are now called with the latest state values. Previously there were only ever called with the initial left sidebar state value (due to a stale closure)
22
+
23
+ **Improvements**
24
+
25
+ - no longer possible to trigger the collapse of the sidebar when it is already collapsed
26
+ - no longer possible to trigger an expand of the sidebar when it is already expanded
27
+ - triggering an expand while the sidebar is collapsing will now flush the pending `onLeftSidebarExpand`
28
+ - triggering an collapse while the sidebar is expanding will now flush the pending `onLeftSidebarCollapse`
29
+ - only adding the event listener for `"transitionend"` when the sidebar is expanding or collapsing.
30
+ - removing `"transitionend"` event listener when `<LeftSidebar />` is unmounted
31
+ - explicitly aborting pending collapse / expand actions when `<LeftSidebar />` is unmounted while collapsing / expanding.
32
+
3
33
  ## 1.4.0
4
34
 
5
35
  ### Minor Changes
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.SidebarResizeController = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
+ var _bindEventListener = require("bind-event-listener");
11
12
  var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
12
13
  var _motion = require("@atlaskit/motion");
13
14
  var _constants = require("../common/constants");
@@ -22,7 +23,7 @@ var handleDataAttributesAndCb = function handleDataAttributesAndCb() {
22
23
  document.documentElement.removeAttribute(_constants.IS_SIDEBAR_COLLAPSING);
23
24
  callback(leftSidebarState);
24
25
  };
25
-
26
+ var leftSidebarSelector = (0, _utils.getPageLayoutSlotCSSSelector)('left-sidebar');
26
27
  // eslint-disable-next-line @repo/internal/react/require-jsdoc
27
28
  var SidebarResizeController = function SidebarResizeController(_ref) {
28
29
  var children = _ref.children,
@@ -41,56 +42,35 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
41
42
  leftSidebarState = _useState2[0],
42
43
  setLeftSidebarState = _useState2[1];
43
44
  var isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
44
- var leftSidebarSelector = (0, _utils.getPageLayoutSlotCSSSelector)('left-sidebar');
45
-
46
- /**
47
- * Bug: this function will cause `onExpand` / `onCollapse` when any
48
- * `width` transition occurs (eg when cancelling a resizing)
49
- * This
50
- */
51
- var transitionEventHandler = (0, _react.useCallback)(function (event) {
52
- if (event.propertyName === 'width' && event.target && event.target.matches(leftSidebarSelector)) {
53
- var $leftSidebarResizeController = document.querySelector("[".concat(_constants.GRAB_AREA_SELECTOR, "]"));
54
- var isCollapsed = !!$leftSidebarResizeController && $leftSidebarResizeController.hasAttribute('disabled');
55
- handleDataAttributesAndCb(
56
- /**
57
- * Bug: `onCollapse` and `onExpand` are stale after the first render
58
- */
59
- isCollapsed ? onCollapse : onExpand, isCollapsed,
60
- /**
61
- * Bug: `leftSidebarState` is stale after the first render
62
- */
63
- leftSidebarState);
64
45
 
65
- /**
66
- * TODO: this appears smelly. Let's do better
67
- */
68
- // Make sure multiple event handlers do not get attached
69
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
70
- document.querySelector(leftSidebarSelector).removeEventListener('transitionend', transitionEventHandler);
71
- }
72
- // eslint-disable-next-line react-hooks/exhaustive-deps
73
- }, []);
46
+ // We put the latest callbacks into a ref so we can always have the latest
47
+ // functions in our transitionend listeners
48
+ var stableRef = (0, _react.useRef)({
49
+ onExpand: onExpand,
50
+ onCollapse: onCollapse
51
+ });
74
52
  (0, _react.useEffect)(function () {
75
- var $leftSidebar = document.querySelector(leftSidebarSelector);
76
- if ($leftSidebar && !(0, _motion.isReducedMotion)()) {
77
- /**
78
- * Note: This pattern relies on `transitionEventHandler` keeping a stable
79
- * reference to continually adding event listeners.
80
- * I think there should be a better way
81
- */
82
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
83
- $leftSidebar.addEventListener('transitionend', transitionEventHandler);
84
- }
85
- }, [isLeftSidebarCollapsed, leftSidebarSelector, leftSidebarState, onCollapse, onExpand, transitionEventHandler]);
53
+ stableRef.current = {
54
+ onExpand: onExpand,
55
+ onCollapse: onCollapse
56
+ };
57
+ });
58
+ var transition = (0, _react.useRef)(null);
86
59
  var expandLeftSidebar = (0, _react.useCallback)(function () {
60
+ var _transition$current, _transition$current2;
87
61
  var lastLeftSidebarWidth = leftSidebarState.lastLeftSidebarWidth,
88
62
  isResizing = leftSidebarState.isResizing,
89
63
  flyoutLockCount = leftSidebarState.flyoutLockCount,
90
- isFixed = leftSidebarState.isFixed;
91
- if (isResizing) {
64
+ isFixed = leftSidebarState.isFixed,
65
+ isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
66
+ if (isResizing || !isLeftSidebarCollapsed ||
67
+ // already expanding
68
+ ((_transition$current = transition.current) === null || _transition$current === void 0 ? void 0 : _transition$current.action) === 'expand') {
92
69
  return;
93
70
  }
71
+
72
+ // flush existing transition
73
+ (_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.complete();
94
74
  var width = Math.max(lastLeftSidebarWidth, _constants.DEFAULT_LEFT_SIDEBAR_WIDTH);
95
75
  var updatedLeftSidebarState = {
96
76
  isLeftSidebarCollapsed: false,
@@ -102,22 +82,55 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
102
82
  isFixed: isFixed
103
83
  };
104
84
  setLeftSidebarState(updatedLeftSidebarState);
105
-
85
+ function finish() {
86
+ handleDataAttributesAndCb(stableRef.current.onExpand, false,
87
+ // isCollapsed
88
+ updatedLeftSidebarState);
89
+ }
90
+ var sidebar = document.querySelector(leftSidebarSelector);
106
91
  // onTransitionEnd isn't triggered when a user prefers reduced motion
107
- if ((0, _motion.isReducedMotion)()) {
108
- handleDataAttributesAndCb(onExpand, false, updatedLeftSidebarState);
92
+ if ((0, _motion.isReducedMotion)() || !sidebar) {
93
+ finish();
94
+ return;
109
95
  }
110
- }, [leftSidebarState, onExpand]);
96
+ var unbindEvent = (0, _bindEventListener.bind)(sidebar, {
97
+ type: 'transitionend',
98
+ listener: function listener(event) {
99
+ 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();
102
+ }
103
+ }
104
+ });
105
+ var value = {
106
+ action: 'expand',
107
+ complete: function complete() {
108
+ value.abort();
109
+ finish();
110
+ },
111
+ abort: function abort() {
112
+ unbindEvent();
113
+ transition.current = null;
114
+ }
115
+ };
116
+ transition.current = value;
117
+ }, [leftSidebarState]);
111
118
  var collapseLeftSidebar = (0, _react.useCallback)(function (event, collapseWithoutTransition) {
119
+ var _transition$current4, _transition$current5;
112
120
  var leftSidebarWidth = leftSidebarState.leftSidebarWidth,
113
121
  isResizing = leftSidebarState.isResizing,
114
122
  flyoutLockCount = leftSidebarState.flyoutLockCount,
115
123
  isFixed = leftSidebarState.isFixed,
116
124
  isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
117
- if (isResizing || isLeftSidebarCollapsed) {
125
+ if (isResizing || isLeftSidebarCollapsed ||
126
+ // already collapsing
127
+ ((_transition$current4 = transition.current) === null || _transition$current4 === void 0 ? void 0 : _transition$current4.action) === 'collapse') {
118
128
  return;
119
129
  }
120
130
 
131
+ // flush existing transition
132
+ (_transition$current5 = transition.current) === null || _transition$current5 === void 0 ? void 0 : _transition$current5.complete();
133
+
121
134
  // data-attribute is used as a CSS selector to sync the hiding/showing
122
135
  // of the nav contents with expand/collapse animation
123
136
  document.documentElement.setAttribute(_constants.IS_SIDEBAR_COLLAPSING, 'true');
@@ -131,12 +144,46 @@ var SidebarResizeController = function SidebarResizeController(_ref) {
131
144
  isFixed: isFixed
132
145
  };
133
146
  setLeftSidebarState(updatedLeftSidebarState);
147
+ function finish() {
148
+ handleDataAttributesAndCb(stableRef.current.onCollapse, true, updatedLeftSidebarState);
149
+ }
150
+ var sidebar = document.querySelector(leftSidebarSelector);
134
151
 
135
152
  // onTransitionEnd isn't triggered when a user prefers reduced motion
136
- if (collapseWithoutTransition || (0, _motion.isReducedMotion)()) {
137
- handleDataAttributesAndCb(onCollapse, true, updatedLeftSidebarState);
153
+ if (collapseWithoutTransition || (0, _motion.isReducedMotion)() || !sidebar) {
154
+ finish();
155
+ return;
138
156
  }
139
- }, [leftSidebarState, onCollapse]);
157
+ var unbindEvent = (0, _bindEventListener.bind)(sidebar, {
158
+ type: 'transitionend',
159
+ listener: function listener(event) {
160
+ 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();
163
+ }
164
+ }
165
+ });
166
+ var value = {
167
+ action: 'collapse',
168
+ complete: function complete() {
169
+ value.abort();
170
+ finish();
171
+ },
172
+ abort: function abort() {
173
+ unbindEvent();
174
+ transition.current = null;
175
+ }
176
+ };
177
+ transition.current = value;
178
+ }, [leftSidebarState]);
179
+
180
+ // Make sure we finish any lingering transitions when unmounting
181
+ (0, _react.useEffect)(function mount() {
182
+ return function unmount() {
183
+ var _transition$current7;
184
+ (_transition$current7 = transition.current) === null || _transition$current7 === void 0 ? void 0 : _transition$current7.abort();
185
+ };
186
+ }, []);
140
187
  var context = (0, _react.useMemo)(function () {
141
188
  return {
142
189
  isLeftSidebarCollapsed: isLeftSidebarCollapsed,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,14 +1,15 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { bind } from 'bind-event-listener';
2
3
  import noop from '@atlaskit/ds-lib/noop';
3
4
  import { isReducedMotion } from '@atlaskit/motion';
4
- import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, GRAB_AREA_SELECTOR, IS_SIDEBAR_COLLAPSING } from '../common/constants';
5
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, IS_SIDEBAR_COLLAPSING } from '../common/constants';
5
6
  import { getPageLayoutSlotCSSSelector } from '../common/utils';
6
7
  import { SidebarResizeContext } from './sidebar-resize-context';
7
8
  const handleDataAttributesAndCb = (callback = noop, isLeftSidebarCollapsed, leftSidebarState) => {
8
9
  document.documentElement.removeAttribute(IS_SIDEBAR_COLLAPSING);
9
10
  callback(leftSidebarState);
10
11
  };
11
-
12
+ const leftSidebarSelector = getPageLayoutSlotCSSSelector('left-sidebar');
12
13
  // eslint-disable-next-line @repo/internal/react/require-jsdoc
13
14
  export const SidebarResizeController = ({
14
15
  children,
@@ -27,58 +28,37 @@ export const SidebarResizeController = ({
27
28
  const {
28
29
  isLeftSidebarCollapsed
29
30
  } = leftSidebarState;
30
- const leftSidebarSelector = getPageLayoutSlotCSSSelector('left-sidebar');
31
-
32
- /**
33
- * Bug: this function will cause `onExpand` / `onCollapse` when any
34
- * `width` transition occurs (eg when cancelling a resizing)
35
- * This
36
- */
37
- const transitionEventHandler = useCallback(event => {
38
- if (event.propertyName === 'width' && event.target && event.target.matches(leftSidebarSelector)) {
39
- const $leftSidebarResizeController = document.querySelector(`[${GRAB_AREA_SELECTOR}]`);
40
- const isCollapsed = !!$leftSidebarResizeController && $leftSidebarResizeController.hasAttribute('disabled');
41
- handleDataAttributesAndCb(
42
- /**
43
- * Bug: `onCollapse` and `onExpand` are stale after the first render
44
- */
45
- isCollapsed ? onCollapse : onExpand, isCollapsed,
46
- /**
47
- * Bug: `leftSidebarState` is stale after the first render
48
- */
49
- leftSidebarState);
50
31
 
51
- /**
52
- * TODO: this appears smelly. Let's do better
53
- */
54
- // Make sure multiple event handlers do not get attached
55
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
56
- document.querySelector(leftSidebarSelector).removeEventListener('transitionend', transitionEventHandler);
57
- }
58
- // eslint-disable-next-line react-hooks/exhaustive-deps
59
- }, []);
32
+ // We put the latest callbacks into a ref so we can always have the latest
33
+ // functions in our transitionend listeners
34
+ const stableRef = useRef({
35
+ onExpand,
36
+ onCollapse
37
+ });
60
38
  useEffect(() => {
61
- const $leftSidebar = document.querySelector(leftSidebarSelector);
62
- if ($leftSidebar && !isReducedMotion()) {
63
- /**
64
- * Note: This pattern relies on `transitionEventHandler` keeping a stable
65
- * reference to continually adding event listeners.
66
- * I think there should be a better way
67
- */
68
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
69
- $leftSidebar.addEventListener('transitionend', transitionEventHandler);
70
- }
71
- }, [isLeftSidebarCollapsed, leftSidebarSelector, leftSidebarState, onCollapse, onExpand, transitionEventHandler]);
39
+ stableRef.current = {
40
+ onExpand,
41
+ onCollapse
42
+ };
43
+ });
44
+ const transition = useRef(null);
72
45
  const expandLeftSidebar = useCallback(() => {
46
+ var _transition$current, _transition$current2;
73
47
  const {
74
48
  lastLeftSidebarWidth,
75
49
  isResizing,
76
50
  flyoutLockCount,
77
- isFixed
51
+ isFixed,
52
+ isLeftSidebarCollapsed
78
53
  } = leftSidebarState;
79
- if (isResizing) {
54
+ if (isResizing || !isLeftSidebarCollapsed ||
55
+ // already expanding
56
+ ((_transition$current = transition.current) === null || _transition$current === void 0 ? void 0 : _transition$current.action) === 'expand') {
80
57
  return;
81
58
  }
59
+
60
+ // flush existing transition
61
+ (_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.complete();
82
62
  const width = Math.max(lastLeftSidebarWidth, DEFAULT_LEFT_SIDEBAR_WIDTH);
83
63
  const updatedLeftSidebarState = {
84
64
  isLeftSidebarCollapsed: false,
@@ -90,13 +70,41 @@ export const SidebarResizeController = ({
90
70
  isFixed
91
71
  };
92
72
  setLeftSidebarState(updatedLeftSidebarState);
93
-
73
+ function finish() {
74
+ handleDataAttributesAndCb(stableRef.current.onExpand, false,
75
+ // isCollapsed
76
+ updatedLeftSidebarState);
77
+ }
78
+ const sidebar = document.querySelector(leftSidebarSelector);
94
79
  // onTransitionEnd isn't triggered when a user prefers reduced motion
95
- if (isReducedMotion()) {
96
- handleDataAttributesAndCb(onExpand, false, updatedLeftSidebarState);
80
+ if (isReducedMotion() || !sidebar) {
81
+ finish();
82
+ return;
97
83
  }
98
- }, [leftSidebarState, onExpand]);
84
+ const unbindEvent = bind(sidebar, {
85
+ type: 'transitionend',
86
+ listener(event) {
87
+ if (event.target === sidebar && event.propertyName === 'width') {
88
+ var _transition$current3;
89
+ (_transition$current3 = transition.current) === null || _transition$current3 === void 0 ? void 0 : _transition$current3.complete();
90
+ }
91
+ }
92
+ });
93
+ const value = {
94
+ action: 'expand',
95
+ complete: () => {
96
+ value.abort();
97
+ finish();
98
+ },
99
+ abort: () => {
100
+ unbindEvent();
101
+ transition.current = null;
102
+ }
103
+ };
104
+ transition.current = value;
105
+ }, [leftSidebarState]);
99
106
  const collapseLeftSidebar = useCallback((event, collapseWithoutTransition) => {
107
+ var _transition$current4, _transition$current5;
100
108
  const {
101
109
  leftSidebarWidth,
102
110
  isResizing,
@@ -104,10 +112,15 @@ export const SidebarResizeController = ({
104
112
  isFixed,
105
113
  isLeftSidebarCollapsed
106
114
  } = leftSidebarState;
107
- if (isResizing || isLeftSidebarCollapsed) {
115
+ if (isResizing || isLeftSidebarCollapsed ||
116
+ // already collapsing
117
+ ((_transition$current4 = transition.current) === null || _transition$current4 === void 0 ? void 0 : _transition$current4.action) === 'collapse') {
108
118
  return;
109
119
  }
110
120
 
121
+ // flush existing transition
122
+ (_transition$current5 = transition.current) === null || _transition$current5 === void 0 ? void 0 : _transition$current5.complete();
123
+
111
124
  // data-attribute is used as a CSS selector to sync the hiding/showing
112
125
  // of the nav contents with expand/collapse animation
113
126
  document.documentElement.setAttribute(IS_SIDEBAR_COLLAPSING, 'true');
@@ -121,12 +134,46 @@ export const SidebarResizeController = ({
121
134
  isFixed
122
135
  };
123
136
  setLeftSidebarState(updatedLeftSidebarState);
137
+ function finish() {
138
+ handleDataAttributesAndCb(stableRef.current.onCollapse, true, updatedLeftSidebarState);
139
+ }
140
+ const sidebar = document.querySelector(leftSidebarSelector);
124
141
 
125
142
  // onTransitionEnd isn't triggered when a user prefers reduced motion
126
- if (collapseWithoutTransition || isReducedMotion()) {
127
- handleDataAttributesAndCb(onCollapse, true, updatedLeftSidebarState);
143
+ if (collapseWithoutTransition || isReducedMotion() || !sidebar) {
144
+ finish();
145
+ return;
128
146
  }
129
- }, [leftSidebarState, onCollapse]);
147
+ const unbindEvent = bind(sidebar, {
148
+ type: 'transitionend',
149
+ listener(event) {
150
+ if (sidebar === event.target && event.propertyName === 'width') {
151
+ var _transition$current6;
152
+ (_transition$current6 = transition.current) === null || _transition$current6 === void 0 ? void 0 : _transition$current6.complete();
153
+ }
154
+ }
155
+ });
156
+ const value = {
157
+ action: 'collapse',
158
+ complete: () => {
159
+ value.abort();
160
+ finish();
161
+ },
162
+ abort: () => {
163
+ unbindEvent();
164
+ transition.current = null;
165
+ }
166
+ };
167
+ transition.current = value;
168
+ }, [leftSidebarState]);
169
+
170
+ // Make sure we finish any lingering transitions when unmounting
171
+ useEffect(function mount() {
172
+ return function unmount() {
173
+ var _transition$current7;
174
+ (_transition$current7 = transition.current) === null || _transition$current7 === void 0 ? void 0 : _transition$current7.abort();
175
+ };
176
+ }, []);
130
177
  const context = useMemo(() => ({
131
178
  isLeftSidebarCollapsed,
132
179
  expandLeftSidebar,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,8 +1,9 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { bind } from 'bind-event-listener';
3
4
  import noop from '@atlaskit/ds-lib/noop';
4
5
  import { isReducedMotion } from '@atlaskit/motion';
5
- import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, GRAB_AREA_SELECTOR, IS_SIDEBAR_COLLAPSING } from '../common/constants';
6
+ import { COLLAPSED_LEFT_SIDEBAR_WIDTH, DEFAULT_LEFT_SIDEBAR_WIDTH, IS_SIDEBAR_COLLAPSING } from '../common/constants';
6
7
  import { getPageLayoutSlotCSSSelector } from '../common/utils';
7
8
  import { SidebarResizeContext } from './sidebar-resize-context';
8
9
  var handleDataAttributesAndCb = function handleDataAttributesAndCb() {
@@ -12,7 +13,7 @@ var handleDataAttributesAndCb = function handleDataAttributesAndCb() {
12
13
  document.documentElement.removeAttribute(IS_SIDEBAR_COLLAPSING);
13
14
  callback(leftSidebarState);
14
15
  };
15
-
16
+ var leftSidebarSelector = getPageLayoutSlotCSSSelector('left-sidebar');
16
17
  // eslint-disable-next-line @repo/internal/react/require-jsdoc
17
18
  export var SidebarResizeController = function SidebarResizeController(_ref) {
18
19
  var children = _ref.children,
@@ -31,56 +32,35 @@ export var SidebarResizeController = function SidebarResizeController(_ref) {
31
32
  leftSidebarState = _useState2[0],
32
33
  setLeftSidebarState = _useState2[1];
33
34
  var isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
34
- var leftSidebarSelector = getPageLayoutSlotCSSSelector('left-sidebar');
35
-
36
- /**
37
- * Bug: this function will cause `onExpand` / `onCollapse` when any
38
- * `width` transition occurs (eg when cancelling a resizing)
39
- * This
40
- */
41
- var transitionEventHandler = useCallback(function (event) {
42
- if (event.propertyName === 'width' && event.target && event.target.matches(leftSidebarSelector)) {
43
- var $leftSidebarResizeController = document.querySelector("[".concat(GRAB_AREA_SELECTOR, "]"));
44
- var isCollapsed = !!$leftSidebarResizeController && $leftSidebarResizeController.hasAttribute('disabled');
45
- handleDataAttributesAndCb(
46
- /**
47
- * Bug: `onCollapse` and `onExpand` are stale after the first render
48
- */
49
- isCollapsed ? onCollapse : onExpand, isCollapsed,
50
- /**
51
- * Bug: `leftSidebarState` is stale after the first render
52
- */
53
- leftSidebarState);
54
35
 
55
- /**
56
- * TODO: this appears smelly. Let's do better
57
- */
58
- // Make sure multiple event handlers do not get attached
59
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
60
- document.querySelector(leftSidebarSelector).removeEventListener('transitionend', transitionEventHandler);
61
- }
62
- // eslint-disable-next-line react-hooks/exhaustive-deps
63
- }, []);
36
+ // We put the latest callbacks into a ref so we can always have the latest
37
+ // functions in our transitionend listeners
38
+ var stableRef = useRef({
39
+ onExpand: onExpand,
40
+ onCollapse: onCollapse
41
+ });
64
42
  useEffect(function () {
65
- var $leftSidebar = document.querySelector(leftSidebarSelector);
66
- if ($leftSidebar && !isReducedMotion()) {
67
- /**
68
- * Note: This pattern relies on `transitionEventHandler` keeping a stable
69
- * reference to continually adding event listeners.
70
- * I think there should be a better way
71
- */
72
- // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
73
- $leftSidebar.addEventListener('transitionend', transitionEventHandler);
74
- }
75
- }, [isLeftSidebarCollapsed, leftSidebarSelector, leftSidebarState, onCollapse, onExpand, transitionEventHandler]);
43
+ stableRef.current = {
44
+ onExpand: onExpand,
45
+ onCollapse: onCollapse
46
+ };
47
+ });
48
+ var transition = useRef(null);
76
49
  var expandLeftSidebar = useCallback(function () {
50
+ var _transition$current, _transition$current2;
77
51
  var lastLeftSidebarWidth = leftSidebarState.lastLeftSidebarWidth,
78
52
  isResizing = leftSidebarState.isResizing,
79
53
  flyoutLockCount = leftSidebarState.flyoutLockCount,
80
- isFixed = leftSidebarState.isFixed;
81
- if (isResizing) {
54
+ isFixed = leftSidebarState.isFixed,
55
+ isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
56
+ if (isResizing || !isLeftSidebarCollapsed ||
57
+ // already expanding
58
+ ((_transition$current = transition.current) === null || _transition$current === void 0 ? void 0 : _transition$current.action) === 'expand') {
82
59
  return;
83
60
  }
61
+
62
+ // flush existing transition
63
+ (_transition$current2 = transition.current) === null || _transition$current2 === void 0 ? void 0 : _transition$current2.complete();
84
64
  var width = Math.max(lastLeftSidebarWidth, DEFAULT_LEFT_SIDEBAR_WIDTH);
85
65
  var updatedLeftSidebarState = {
86
66
  isLeftSidebarCollapsed: false,
@@ -92,22 +72,55 @@ export var SidebarResizeController = function SidebarResizeController(_ref) {
92
72
  isFixed: isFixed
93
73
  };
94
74
  setLeftSidebarState(updatedLeftSidebarState);
95
-
75
+ function finish() {
76
+ handleDataAttributesAndCb(stableRef.current.onExpand, false,
77
+ // isCollapsed
78
+ updatedLeftSidebarState);
79
+ }
80
+ var sidebar = document.querySelector(leftSidebarSelector);
96
81
  // onTransitionEnd isn't triggered when a user prefers reduced motion
97
- if (isReducedMotion()) {
98
- handleDataAttributesAndCb(onExpand, false, updatedLeftSidebarState);
82
+ if (isReducedMotion() || !sidebar) {
83
+ finish();
84
+ return;
99
85
  }
100
- }, [leftSidebarState, onExpand]);
86
+ var unbindEvent = bind(sidebar, {
87
+ type: 'transitionend',
88
+ listener: function listener(event) {
89
+ if (event.target === sidebar && event.propertyName === 'width') {
90
+ var _transition$current3;
91
+ (_transition$current3 = transition.current) === null || _transition$current3 === void 0 ? void 0 : _transition$current3.complete();
92
+ }
93
+ }
94
+ });
95
+ var value = {
96
+ action: 'expand',
97
+ complete: function complete() {
98
+ value.abort();
99
+ finish();
100
+ },
101
+ abort: function abort() {
102
+ unbindEvent();
103
+ transition.current = null;
104
+ }
105
+ };
106
+ transition.current = value;
107
+ }, [leftSidebarState]);
101
108
  var collapseLeftSidebar = useCallback(function (event, collapseWithoutTransition) {
109
+ var _transition$current4, _transition$current5;
102
110
  var leftSidebarWidth = leftSidebarState.leftSidebarWidth,
103
111
  isResizing = leftSidebarState.isResizing,
104
112
  flyoutLockCount = leftSidebarState.flyoutLockCount,
105
113
  isFixed = leftSidebarState.isFixed,
106
114
  isLeftSidebarCollapsed = leftSidebarState.isLeftSidebarCollapsed;
107
- if (isResizing || isLeftSidebarCollapsed) {
115
+ if (isResizing || isLeftSidebarCollapsed ||
116
+ // already collapsing
117
+ ((_transition$current4 = transition.current) === null || _transition$current4 === void 0 ? void 0 : _transition$current4.action) === 'collapse') {
108
118
  return;
109
119
  }
110
120
 
121
+ // flush existing transition
122
+ (_transition$current5 = transition.current) === null || _transition$current5 === void 0 ? void 0 : _transition$current5.complete();
123
+
111
124
  // data-attribute is used as a CSS selector to sync the hiding/showing
112
125
  // of the nav contents with expand/collapse animation
113
126
  document.documentElement.setAttribute(IS_SIDEBAR_COLLAPSING, 'true');
@@ -121,12 +134,46 @@ export var SidebarResizeController = function SidebarResizeController(_ref) {
121
134
  isFixed: isFixed
122
135
  };
123
136
  setLeftSidebarState(updatedLeftSidebarState);
137
+ function finish() {
138
+ handleDataAttributesAndCb(stableRef.current.onCollapse, true, updatedLeftSidebarState);
139
+ }
140
+ var sidebar = document.querySelector(leftSidebarSelector);
124
141
 
125
142
  // onTransitionEnd isn't triggered when a user prefers reduced motion
126
- if (collapseWithoutTransition || isReducedMotion()) {
127
- handleDataAttributesAndCb(onCollapse, true, updatedLeftSidebarState);
143
+ if (collapseWithoutTransition || isReducedMotion() || !sidebar) {
144
+ finish();
145
+ return;
128
146
  }
129
- }, [leftSidebarState, onCollapse]);
147
+ var unbindEvent = bind(sidebar, {
148
+ type: 'transitionend',
149
+ listener: function listener(event) {
150
+ if (sidebar === event.target && event.propertyName === 'width') {
151
+ var _transition$current6;
152
+ (_transition$current6 = transition.current) === null || _transition$current6 === void 0 ? void 0 : _transition$current6.complete();
153
+ }
154
+ }
155
+ });
156
+ var value = {
157
+ action: 'collapse',
158
+ complete: function complete() {
159
+ value.abort();
160
+ finish();
161
+ },
162
+ abort: function abort() {
163
+ unbindEvent();
164
+ transition.current = null;
165
+ }
166
+ };
167
+ transition.current = value;
168
+ }, [leftSidebarState]);
169
+
170
+ // Make sure we finish any lingering transitions when unmounting
171
+ useEffect(function mount() {
172
+ return function unmount() {
173
+ var _transition$current7;
174
+ (_transition$current7 = transition.current) === null || _transition$current7 === void 0 ? void 0 : _transition$current7.abort();
175
+ };
176
+ }, []);
130
177
  var context = useMemo(function () {
131
178
  return {
132
179
  isLeftSidebarCollapsed: isLeftSidebarCollapsed,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "sideEffects": false
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/page-layout",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "A collection of components which let you compose an application's page layout.",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -25,11 +25,11 @@
25
25
  },
26
26
  "homepage": "https://atlassian.design/components/page-layout/",
27
27
  "dependencies": {
28
- "@atlaskit/ds-lib": "^2.1.0",
29
- "@atlaskit/icon": "^21.11.0",
30
- "@atlaskit/motion": "^1.3.0",
31
- "@atlaskit/theme": "^12.4.0",
32
- "@atlaskit/tokens": "^1.2.0",
28
+ "@atlaskit/ds-lib": "^2.2.0",
29
+ "@atlaskit/icon": "^21.12.0",
30
+ "@atlaskit/motion": "^1.4.0",
31
+ "@atlaskit/theme": "^12.5.0",
32
+ "@atlaskit/tokens": "^1.3.0",
33
33
  "@babel/runtime": "^7.0.0",
34
34
  "@emotion/react": "^11.7.1",
35
35
  "bind-event-listener": "^2.1.1",
@@ -40,20 +40,20 @@
40
40
  "react-dom": "^16.8.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@atlaskit/atlassian-navigation": "^2.4.0",
44
- "@atlaskit/atlassian-notifications": "^0.3.0",
45
- "@atlaskit/button": "^16.6.0",
43
+ "@atlaskit/atlassian-navigation": "^2.5.0",
44
+ "@atlaskit/atlassian-notifications": "^0.4.0",
45
+ "@atlaskit/button": "^16.7.0",
46
46
  "@atlaskit/docs": "*",
47
- "@atlaskit/drawer": "^7.4.0",
47
+ "@atlaskit/drawer": "^7.5.0",
48
48
  "@atlaskit/icon": "*",
49
- "@atlaskit/logo": "^13.13.0",
50
- "@atlaskit/menu": "^1.5.0",
51
- "@atlaskit/notification-indicator": "^9.0.0",
52
- "@atlaskit/notification-log-client": "^6.0.0",
53
- "@atlaskit/onboarding": "^10.7.0",
54
- "@atlaskit/popup": "^1.5.0",
55
- "@atlaskit/section-message": "^6.3.0",
56
- "@atlaskit/side-navigation": "^1.6.0",
49
+ "@atlaskit/logo": "^13.14.0",
50
+ "@atlaskit/menu": "^1.6.0",
51
+ "@atlaskit/notification-indicator": "^9.1.0",
52
+ "@atlaskit/notification-log-client": "^6.1.0",
53
+ "@atlaskit/onboarding": "^10.8.0",
54
+ "@atlaskit/popup": "^1.6.0",
55
+ "@atlaskit/section-message": "^6.4.0",
56
+ "@atlaskit/side-navigation": "^1.7.0",
57
57
  "@atlaskit/ssr": "*",
58
58
  "@atlaskit/tooltip": "*",
59
59
  "@atlaskit/visual-regression": "*",