@atlaskit/navigation-system 4.2.0 → 4.4.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 +17 -0
- package/dist/cjs/ui/page-layout/root.js +1 -2
- package/dist/cjs/ui/page-layout/side-nav/side-nav.js +70 -3
- package/dist/cjs/ui/page-layout/side-nav/toggle-button.js +5 -3
- package/dist/cjs/ui/page-layout/side-nav/visibility-provider.js +1 -1
- package/dist/cjs/ui/page-layout/top-nav/top-nav-start.js +10 -1
- package/dist/es2019/ui/page-layout/root.js +1 -1
- package/dist/es2019/ui/page-layout/side-nav/side-nav.js +70 -4
- package/dist/es2019/ui/page-layout/side-nav/toggle-button.js +5 -3
- package/dist/es2019/ui/page-layout/side-nav/visibility-provider.js +1 -1
- package/dist/es2019/ui/page-layout/top-nav/top-nav-start.js +10 -1
- package/dist/esm/ui/page-layout/root.js +1 -2
- package/dist/esm/ui/page-layout/side-nav/side-nav.js +70 -4
- package/dist/esm/ui/page-layout/side-nav/toggle-button.js +5 -3
- package/dist/esm/ui/page-layout/side-nav/visibility-provider.js +1 -1
- package/dist/esm/ui/page-layout/top-nav/top-nav-start.js +10 -1
- package/dist/types/ui/page-layout/root.d.ts +1 -1
- package/dist/types/ui/page-layout/side-nav/side-nav.d.ts +13 -2
- package/dist/types/ui/page-layout/side-nav/toggle-button.d.ts +1 -1
- package/dist/types/ui/page-layout/side-nav/visibility-provider.d.ts +1 -1
- package/dist/types/ui/page-layout/top-nav/top-nav-start.d.ts +1 -1
- package/dist/types-ts4.5/ui/page-layout/root.d.ts +1 -1
- package/dist/types-ts4.5/ui/page-layout/side-nav/side-nav.d.ts +13 -2
- package/dist/types-ts4.5/ui/page-layout/side-nav/toggle-button.d.ts +1 -1
- package/dist/types-ts4.5/ui/page-layout/side-nav/visibility-provider.d.ts +1 -1
- package/dist/types-ts4.5/ui/page-layout/top-nav/top-nav-start.d.ts +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlassian/navigation-system
|
|
2
2
|
|
|
3
|
+
## 4.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`327d6a06eebb2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/327d6a06eebb2) -
|
|
8
|
+
Adds `onPeekStart` and `onPeekEnd` callbacks to the `SideNav` to use for monitoring when users
|
|
9
|
+
peek at the side navigation when it is collapsed.
|
|
10
|
+
|
|
11
|
+
## 4.3.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`4bb7b31bfda3b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4bb7b31bfda3b) -
|
|
16
|
+
Refactors changes that were previously behind `platform_dst_nav4_full_height_sidebar_api_changes`
|
|
17
|
+
to now be behind `platform_dst_nav4_side_nav_default_collapsed_api`. After rollout, both the new
|
|
18
|
+
and legacy APIs for defining side nav initial state will be supported.
|
|
19
|
+
|
|
3
20
|
## 4.2.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
|
@@ -36,8 +36,7 @@ function Root(_ref) {
|
|
|
36
36
|
_ref$skipLinksLabel = _ref.skipLinksLabel,
|
|
37
37
|
skipLinksLabel = _ref$skipLinksLabel === void 0 ? 'Skip to:' : _ref$skipLinksLabel,
|
|
38
38
|
testId = _ref.testId,
|
|
39
|
-
|
|
40
|
-
defaultSideNavCollapsed = _ref$defaultSideNavCo === void 0 ? false : _ref$defaultSideNavCo;
|
|
39
|
+
defaultSideNavCollapsed = _ref.defaultSideNavCollapsed;
|
|
41
40
|
var ref = (0, _react.useRef)(null);
|
|
42
41
|
(0, _react.useEffect)(function () {
|
|
43
42
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
9
|
exports.SideNav = SideNav;
|
|
10
|
+
exports.onPeekStartDelayMs = void 0;
|
|
10
11
|
require("./side-nav.compiled.css");
|
|
11
12
|
var _react = _interopRequireWildcard(require("react"));
|
|
12
13
|
var React = _react;
|
|
@@ -17,6 +18,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
17
18
|
var _bindEventListener = require("bind-event-listener");
|
|
18
19
|
var _reactDom = require("react-dom");
|
|
19
20
|
var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
|
|
21
|
+
var _useStableRef = _interopRequireDefault(require("@atlaskit/ds-lib/use-stable-ref"));
|
|
20
22
|
var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
|
|
21
23
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
22
24
|
var _adapter = require("@atlaskit/pragmatic-drag-and-drop/element/adapter");
|
|
@@ -77,6 +79,7 @@ var styles = {
|
|
|
77
79
|
fullHeightSidebar: "_165t56xv _180k1wjm _26vxoned _1mt19dtb"
|
|
78
80
|
};
|
|
79
81
|
var fallbackDefaultWidth = 320;
|
|
82
|
+
var onPeekStartDelayMs = exports.onPeekStartDelayMs = 500;
|
|
80
83
|
|
|
81
84
|
/**
|
|
82
85
|
* We need an additional component layer so we can wrap the side nav in a `OpenLayerObserver` and have access to the
|
|
@@ -95,6 +98,8 @@ function SideNavInternal(_ref) {
|
|
|
95
98
|
skipLinkLabel = _ref$skipLinkLabel === void 0 ? label : _ref$skipLinkLabel,
|
|
96
99
|
onExpand = _ref.onExpand,
|
|
97
100
|
onCollapse = _ref.onCollapse,
|
|
101
|
+
onPeekStart = _ref.onPeekStart,
|
|
102
|
+
onPeekEnd = _ref.onPeekEnd,
|
|
98
103
|
providedId = _ref.id;
|
|
99
104
|
var id = (0, _idUtils.useLayoutId)({
|
|
100
105
|
providedId: providedId
|
|
@@ -161,6 +166,16 @@ function SideNavInternal(_ref) {
|
|
|
161
166
|
type: 'not-active'
|
|
162
167
|
});
|
|
163
168
|
var isFlyoutVisible = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open';
|
|
169
|
+
var isExpandedOnDesktopRef = (0, _useStableRef.default)(isExpandedOnDesktop);
|
|
170
|
+
var hasPeekStartedRef = (0, _react.useRef)(false);
|
|
171
|
+
var onPeekStartRef = (0, _useStableRef.default)(onPeekStart);
|
|
172
|
+
var onPeekEndRef = (0, _useStableRef.default)(onPeekEnd);
|
|
173
|
+
var onPeekStartTimeoutIdRef = (0, _react.useRef)(undefined);
|
|
174
|
+
(0, _react.useEffect)(function () {
|
|
175
|
+
return function () {
|
|
176
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
177
|
+
};
|
|
178
|
+
}, []);
|
|
164
179
|
var updateFlyoutState = (0, _react.useMemo)(function () {
|
|
165
180
|
function tryAbortPendingClose() {
|
|
166
181
|
if (flyoutStateRef.current.type === 'waiting-for-close') {
|
|
@@ -168,6 +183,7 @@ function SideNavInternal(_ref) {
|
|
|
168
183
|
}
|
|
169
184
|
}
|
|
170
185
|
function open() {
|
|
186
|
+
var prevFlyoutState = flyoutStateRef.current;
|
|
171
187
|
tryAbortPendingClose();
|
|
172
188
|
flyoutStateRef.current = {
|
|
173
189
|
type: 'open'
|
|
@@ -180,8 +196,24 @@ function SideNavInternal(_ref) {
|
|
|
180
196
|
}
|
|
181
197
|
return currentState;
|
|
182
198
|
});
|
|
199
|
+
|
|
200
|
+
// Avoid redundant calls to `onPeekStart()`
|
|
201
|
+
if (prevFlyoutState.type === 'not-active') {
|
|
202
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
203
|
+
onPeekStartTimeoutIdRef.current = setTimeout(function () {
|
|
204
|
+
var _onPeekStartRef$curre;
|
|
205
|
+
// If the flyout isn't still open after ~500ms then we won't count the peek
|
|
206
|
+
// As we want to track user intention rather than all hovers
|
|
207
|
+
if (isExpandedOnDesktopRef.current || flyoutStateRef.current.type !== 'open') {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
hasPeekStartedRef.current = true;
|
|
211
|
+
(_onPeekStartRef$curre = onPeekStartRef.current) === null || _onPeekStartRef$curre === void 0 || _onPeekStartRef$curre.call(onPeekStartRef);
|
|
212
|
+
}, onPeekStartDelayMs);
|
|
213
|
+
}
|
|
183
214
|
}
|
|
184
215
|
function close() {
|
|
216
|
+
var prevFlyoutState = flyoutStateRef.current;
|
|
185
217
|
tryAbortPendingClose();
|
|
186
218
|
flyoutStateRef.current = {
|
|
187
219
|
type: 'not-active'
|
|
@@ -194,6 +226,15 @@ function SideNavInternal(_ref) {
|
|
|
194
226
|
}
|
|
195
227
|
return currentState;
|
|
196
228
|
});
|
|
229
|
+
|
|
230
|
+
// Avoid redundant calls to `onPeekEnd()`
|
|
231
|
+
if (prevFlyoutState.type !== 'not-active' && hasPeekStartedRef.current) {
|
|
232
|
+
var _onPeekEndRef$current;
|
|
233
|
+
hasPeekStartedRef.current = false;
|
|
234
|
+
(_onPeekEndRef$current = onPeekEndRef.current) === null || _onPeekEndRef$current === void 0 || _onPeekEndRef$current.call(onPeekEndRef, {
|
|
235
|
+
trigger: isExpandedOnDesktopRef.current ? 'side-nav-expand' : 'mouse-leave'
|
|
236
|
+
});
|
|
237
|
+
}
|
|
197
238
|
}
|
|
198
239
|
return function onAction(action) {
|
|
199
240
|
if (action === 'drag-from-flyout-started') {
|
|
@@ -252,7 +293,7 @@ function SideNavInternal(_ref) {
|
|
|
252
293
|
return;
|
|
253
294
|
}
|
|
254
295
|
};
|
|
255
|
-
}, [openLayerObserver, setSideNavState]);
|
|
296
|
+
}, [isExpandedOnDesktopRef, onPeekEndRef, onPeekStartRef, openLayerObserver, setSideNavState]);
|
|
256
297
|
var toggleVisibilityByScreenResize = (0, _useToggleSideNav.useToggleSideNav)({
|
|
257
298
|
trigger: 'screen-resize'
|
|
258
299
|
});
|
|
@@ -260,8 +301,8 @@ function SideNavInternal(_ref) {
|
|
|
260
301
|
trigger: 'click-outside-on-mobile'
|
|
261
302
|
});
|
|
262
303
|
(0, _react.useEffect)(function () {
|
|
263
|
-
if ((0, _platformFeatureFlags.fg)('
|
|
264
|
-
//
|
|
304
|
+
if ((0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
305
|
+
// This is the old version of the hook, so we skip it when the flag is enabled
|
|
265
306
|
return;
|
|
266
307
|
}
|
|
267
308
|
|
|
@@ -274,6 +315,28 @@ function SideNavInternal(_ref) {
|
|
|
274
315
|
lastTrigger: null
|
|
275
316
|
});
|
|
276
317
|
}, [initialDefaultCollapsed, setSideNavState]);
|
|
318
|
+
|
|
319
|
+
// Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
|
|
320
|
+
// For SSR the new API is still necessary
|
|
321
|
+
(0, _react.useLayoutEffect)(function () {
|
|
322
|
+
if (!(0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
323
|
+
// This is the new version of the hook, so we skip it when the flag is disabled
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (sideNavState !== null) {
|
|
327
|
+
// Only need to do an initial sync if it hasn't been initialized from Root
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
|
|
332
|
+
// after SSR hydration. This should only run once, after the initial render on the client.
|
|
333
|
+
setSideNavState({
|
|
334
|
+
desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
|
|
335
|
+
mobile: 'collapsed',
|
|
336
|
+
flyout: 'closed',
|
|
337
|
+
lastTrigger: null
|
|
338
|
+
});
|
|
339
|
+
}, [initialDefaultCollapsed, setSideNavState, sideNavState]);
|
|
277
340
|
var handleExpand = (0, _react.useCallback)(function (_ref2) {
|
|
278
341
|
var screen = _ref2.screen,
|
|
279
342
|
trigger = _ref2.trigger;
|
|
@@ -681,6 +744,8 @@ function SideNav(_ref8) {
|
|
|
681
744
|
skipLinkLabel = _ref8$skipLinkLabel === void 0 ? label : _ref8$skipLinkLabel,
|
|
682
745
|
onExpand = _ref8.onExpand,
|
|
683
746
|
onCollapse = _ref8.onCollapse,
|
|
747
|
+
onPeekStart = _ref8.onPeekStart,
|
|
748
|
+
onPeekEnd = _ref8.onPeekEnd,
|
|
684
749
|
id = _ref8.id;
|
|
685
750
|
return /*#__PURE__*/React.createElement(_openLayerObserver.OpenLayerObserverNamespaceProvider, {
|
|
686
751
|
namespace: openLayerObserverSideNavNamespace
|
|
@@ -692,6 +757,8 @@ function SideNav(_ref8) {
|
|
|
692
757
|
skipLinkLabel: skipLinkLabel,
|
|
693
758
|
onExpand: onExpand,
|
|
694
759
|
onCollapse: onCollapse,
|
|
760
|
+
onPeekStart: onPeekStart,
|
|
761
|
+
onPeekEnd: onPeekEnd,
|
|
695
762
|
id: id
|
|
696
763
|
}, children));
|
|
697
764
|
}
|
|
@@ -20,6 +20,7 @@ var _migration = require("../../top-nav-items/themed/migration");
|
|
|
20
20
|
var _toggleButtonContext = require("./toggle-button-context");
|
|
21
21
|
var _useSideNavVisibility2 = require("./use-side-nav-visibility");
|
|
22
22
|
var _useToggleSideNav = require("./use-toggle-side-nav");
|
|
23
|
+
var _visibilityContext = require("./visibility-context");
|
|
23
24
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
24
25
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
25
26
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -51,10 +52,11 @@ var SideNavToggleButton = exports.SideNavToggleButton = function SideNavToggleBu
|
|
|
51
52
|
}),
|
|
52
53
|
isSideNavExpandedOnDesktop = _useSideNavVisibility.isExpandedOnDesktop,
|
|
53
54
|
isSideNavExpandedOnMobile = _useSideNavVisibility.isExpandedOnMobile;
|
|
55
|
+
var sideNavState = (0, _react.useContext)(_visibilityContext.SideNavVisibilityState);
|
|
54
56
|
|
|
55
|
-
// When `
|
|
56
|
-
//
|
|
57
|
-
var _useState = (0, _react.useState)((0, _platformFeatureFlags.fg)('
|
|
57
|
+
// When default state is provided to `Root` the state in context will already be
|
|
58
|
+
// initialized in SSR
|
|
59
|
+
var _useState = (0, _react.useState)(sideNavState === null || !(0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop),
|
|
58
60
|
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
59
61
|
isSideNavExpanded = _useState2[0],
|
|
60
62
|
setIsSideNavExpanded = _useState2[1];
|
|
@@ -17,7 +17,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
17
17
|
var SideNavVisibilityProvider = exports.SideNavVisibilityProvider = function SideNavVisibilityProvider(_ref) {
|
|
18
18
|
var children = _ref.children,
|
|
19
19
|
defaultCollapsed = _ref.defaultCollapsed;
|
|
20
|
-
var initialState = (0, _platformFeatureFlags.fg)('
|
|
20
|
+
var initialState = typeof defaultCollapsed === 'boolean' && (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_default_collapsed_api') ? {
|
|
21
21
|
desktop: defaultCollapsed ? 'collapsed' : 'expanded',
|
|
22
22
|
mobile: 'collapsed',
|
|
23
23
|
flyout: 'closed',
|
|
@@ -16,6 +16,7 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
|
16
16
|
var _compiled = require("@atlaskit/primitives/compiled");
|
|
17
17
|
var _topNavStartContext = require("../../../context/top-nav-start/top-nav-start-context");
|
|
18
18
|
var _useSideNavVisibility3 = require("../side-nav/use-side-nav-visibility");
|
|
19
|
+
var _visibilityContext = require("../side-nav/visibility-context");
|
|
19
20
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
20
21
|
/**
|
|
21
22
|
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
@@ -180,14 +181,22 @@ function TopNavStart(_ref3) {
|
|
|
180
181
|
|
|
181
182
|
// Used to prevent the reorder animations from running on the initial render.
|
|
182
183
|
var isFirstRenderRef = (0, _react.useRef)(true);
|
|
184
|
+
var sideNavState = (0, _react.useContext)(_visibilityContext.SideNavVisibilityState);
|
|
183
185
|
(0, _react.useEffect)(function () {
|
|
184
186
|
if (!(0, _platformFeatureFlags.fg)('navx-full-height-sidebar')) {
|
|
185
187
|
return;
|
|
186
188
|
}
|
|
189
|
+
|
|
190
|
+
// Ignore renders until the side nav state is initialized
|
|
191
|
+
// So that apps using the legacy API for setting side nav default state do not see
|
|
192
|
+
// animations when they shouldn't
|
|
193
|
+
if (sideNavState === null) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
187
196
|
if (isFirstRenderRef.current) {
|
|
188
197
|
isFirstRenderRef.current = false;
|
|
189
198
|
}
|
|
190
|
-
}, []);
|
|
199
|
+
}, [sideNavState]);
|
|
191
200
|
|
|
192
201
|
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
193
202
|
// can happen a lot (e.g. if the parent re-renders)
|
|
@@ -3,10 +3,11 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
3
3
|
import "./side-nav.compiled.css";
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { ax, ix } from "@compiled/react/runtime";
|
|
6
|
-
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
7
7
|
import { bind } from 'bind-event-listener';
|
|
8
8
|
import { flushSync } from 'react-dom';
|
|
9
9
|
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
10
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
10
11
|
import { OpenLayerObserverNamespaceProvider, useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
|
|
11
12
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
13
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
@@ -64,6 +65,7 @@ const styles = {
|
|
|
64
65
|
fullHeightSidebar: "_165t56xv _180k1wjm _26vxoned _1mt19dtb"
|
|
65
66
|
};
|
|
66
67
|
const fallbackDefaultWidth = 320;
|
|
68
|
+
export const onPeekStartDelayMs = 500;
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* We need an additional component layer so we can wrap the side nav in a `OpenLayerObserver` and have access to the
|
|
@@ -78,6 +80,8 @@ function SideNavInternal({
|
|
|
78
80
|
skipLinkLabel = label,
|
|
79
81
|
onExpand,
|
|
80
82
|
onCollapse,
|
|
83
|
+
onPeekStart,
|
|
84
|
+
onPeekEnd,
|
|
81
85
|
id: providedId
|
|
82
86
|
}) {
|
|
83
87
|
var _sideNavState$lastTri;
|
|
@@ -142,6 +146,16 @@ function SideNavInternal({
|
|
|
142
146
|
type: 'not-active'
|
|
143
147
|
});
|
|
144
148
|
const isFlyoutVisible = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open';
|
|
149
|
+
const isExpandedOnDesktopRef = useStableRef(isExpandedOnDesktop);
|
|
150
|
+
const hasPeekStartedRef = useRef(false);
|
|
151
|
+
const onPeekStartRef = useStableRef(onPeekStart);
|
|
152
|
+
const onPeekEndRef = useStableRef(onPeekEnd);
|
|
153
|
+
const onPeekStartTimeoutIdRef = useRef(undefined);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
return () => {
|
|
156
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
157
|
+
};
|
|
158
|
+
}, []);
|
|
145
159
|
const updateFlyoutState = useMemo(() => {
|
|
146
160
|
function tryAbortPendingClose() {
|
|
147
161
|
if (flyoutStateRef.current.type === 'waiting-for-close') {
|
|
@@ -149,6 +163,7 @@ function SideNavInternal({
|
|
|
149
163
|
}
|
|
150
164
|
}
|
|
151
165
|
function open() {
|
|
166
|
+
const prevFlyoutState = flyoutStateRef.current;
|
|
152
167
|
tryAbortPendingClose();
|
|
153
168
|
flyoutStateRef.current = {
|
|
154
169
|
type: 'open'
|
|
@@ -162,8 +177,24 @@ function SideNavInternal({
|
|
|
162
177
|
}
|
|
163
178
|
return currentState;
|
|
164
179
|
});
|
|
180
|
+
|
|
181
|
+
// Avoid redundant calls to `onPeekStart()`
|
|
182
|
+
if (prevFlyoutState.type === 'not-active') {
|
|
183
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
184
|
+
onPeekStartTimeoutIdRef.current = setTimeout(() => {
|
|
185
|
+
var _onPeekStartRef$curre;
|
|
186
|
+
// If the flyout isn't still open after ~500ms then we won't count the peek
|
|
187
|
+
// As we want to track user intention rather than all hovers
|
|
188
|
+
if (isExpandedOnDesktopRef.current || flyoutStateRef.current.type !== 'open') {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
hasPeekStartedRef.current = true;
|
|
192
|
+
(_onPeekStartRef$curre = onPeekStartRef.current) === null || _onPeekStartRef$curre === void 0 ? void 0 : _onPeekStartRef$curre.call(onPeekStartRef);
|
|
193
|
+
}, onPeekStartDelayMs);
|
|
194
|
+
}
|
|
165
195
|
}
|
|
166
196
|
function close() {
|
|
197
|
+
const prevFlyoutState = flyoutStateRef.current;
|
|
167
198
|
tryAbortPendingClose();
|
|
168
199
|
flyoutStateRef.current = {
|
|
169
200
|
type: 'not-active'
|
|
@@ -177,6 +208,15 @@ function SideNavInternal({
|
|
|
177
208
|
}
|
|
178
209
|
return currentState;
|
|
179
210
|
});
|
|
211
|
+
|
|
212
|
+
// Avoid redundant calls to `onPeekEnd()`
|
|
213
|
+
if (prevFlyoutState.type !== 'not-active' && hasPeekStartedRef.current) {
|
|
214
|
+
var _onPeekEndRef$current;
|
|
215
|
+
hasPeekStartedRef.current = false;
|
|
216
|
+
(_onPeekEndRef$current = onPeekEndRef.current) === null || _onPeekEndRef$current === void 0 ? void 0 : _onPeekEndRef$current.call(onPeekEndRef, {
|
|
217
|
+
trigger: isExpandedOnDesktopRef.current ? 'side-nav-expand' : 'mouse-leave'
|
|
218
|
+
});
|
|
219
|
+
}
|
|
180
220
|
}
|
|
181
221
|
return function onAction(action) {
|
|
182
222
|
if (action === 'drag-from-flyout-started') {
|
|
@@ -235,7 +275,7 @@ function SideNavInternal({
|
|
|
235
275
|
return;
|
|
236
276
|
}
|
|
237
277
|
};
|
|
238
|
-
}, [openLayerObserver, setSideNavState]);
|
|
278
|
+
}, [isExpandedOnDesktopRef, onPeekEndRef, onPeekStartRef, openLayerObserver, setSideNavState]);
|
|
239
279
|
const toggleVisibilityByScreenResize = useToggleSideNav({
|
|
240
280
|
trigger: 'screen-resize'
|
|
241
281
|
});
|
|
@@ -243,8 +283,8 @@ function SideNavInternal({
|
|
|
243
283
|
trigger: 'click-outside-on-mobile'
|
|
244
284
|
});
|
|
245
285
|
useEffect(() => {
|
|
246
|
-
if (fg('
|
|
247
|
-
//
|
|
286
|
+
if (fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
287
|
+
// This is the old version of the hook, so we skip it when the flag is enabled
|
|
248
288
|
return;
|
|
249
289
|
}
|
|
250
290
|
|
|
@@ -257,6 +297,28 @@ function SideNavInternal({
|
|
|
257
297
|
lastTrigger: null
|
|
258
298
|
});
|
|
259
299
|
}, [initialDefaultCollapsed, setSideNavState]);
|
|
300
|
+
|
|
301
|
+
// Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
|
|
302
|
+
// For SSR the new API is still necessary
|
|
303
|
+
useLayoutEffect(() => {
|
|
304
|
+
if (!fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
305
|
+
// This is the new version of the hook, so we skip it when the flag is disabled
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (sideNavState !== null) {
|
|
309
|
+
// Only need to do an initial sync if it hasn't been initialized from Root
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
|
|
314
|
+
// after SSR hydration. This should only run once, after the initial render on the client.
|
|
315
|
+
setSideNavState({
|
|
316
|
+
desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
|
|
317
|
+
mobile: 'collapsed',
|
|
318
|
+
flyout: 'closed',
|
|
319
|
+
lastTrigger: null
|
|
320
|
+
});
|
|
321
|
+
}, [initialDefaultCollapsed, setSideNavState, sideNavState]);
|
|
260
322
|
const handleExpand = useCallback(({
|
|
261
323
|
screen,
|
|
262
324
|
trigger
|
|
@@ -673,6 +735,8 @@ export function SideNav({
|
|
|
673
735
|
// Default value is defined in `SideNavInternal`
|
|
674
736
|
onExpand,
|
|
675
737
|
onCollapse,
|
|
738
|
+
onPeekStart,
|
|
739
|
+
onPeekEnd,
|
|
676
740
|
id
|
|
677
741
|
}) {
|
|
678
742
|
return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
|
|
@@ -685,6 +749,8 @@ export function SideNav({
|
|
|
685
749
|
skipLinkLabel: skipLinkLabel,
|
|
686
750
|
onExpand: onExpand,
|
|
687
751
|
onCollapse: onCollapse,
|
|
752
|
+
onPeekStart: onPeekStart,
|
|
753
|
+
onPeekEnd: onPeekEnd,
|
|
688
754
|
id: id
|
|
689
755
|
}, children));
|
|
690
756
|
}
|
|
@@ -10,6 +10,7 @@ import { IconButton } from '../../top-nav-items/themed/migration';
|
|
|
10
10
|
import { SideNavToggleButtonAttachRef } from './toggle-button-context';
|
|
11
11
|
import { useSideNavVisibility } from './use-side-nav-visibility';
|
|
12
12
|
import { useToggleSideNav } from './use-toggle-side-nav';
|
|
13
|
+
import { SideNavVisibilityState } from './visibility-context';
|
|
13
14
|
const toggleButtonTooltipOptions = {
|
|
14
15
|
// We're disabling pointer events on the tooltip to prevent it from blocking mouse events, so that the side nav flyout stays open
|
|
15
16
|
// when moving the mouse from the top bar to the side nav.
|
|
@@ -39,10 +40,11 @@ export const SideNavToggleButton = ({
|
|
|
39
40
|
} = useSideNavVisibility({
|
|
40
41
|
defaultCollapsed
|
|
41
42
|
});
|
|
43
|
+
const sideNavState = useContext(SideNavVisibilityState);
|
|
42
44
|
|
|
43
|
-
// When `
|
|
44
|
-
//
|
|
45
|
-
const [isSideNavExpanded, setIsSideNavExpanded] = useState(fg('
|
|
45
|
+
// When default state is provided to `Root` the state in context will already be
|
|
46
|
+
// initialized in SSR
|
|
47
|
+
const [isSideNavExpanded, setIsSideNavExpanded] = useState(sideNavState === null || !fg('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop);
|
|
46
48
|
const ref = useContext(SideNavToggleButtonAttachRef);
|
|
47
49
|
const elementRef = useRef(null);
|
|
48
50
|
|
|
@@ -9,7 +9,7 @@ export const SideNavVisibilityProvider = ({
|
|
|
9
9
|
children,
|
|
10
10
|
defaultCollapsed
|
|
11
11
|
}) => {
|
|
12
|
-
const initialState = fg('
|
|
12
|
+
const initialState = typeof defaultCollapsed === 'boolean' && fg('platform_dst_nav4_side_nav_default_collapsed_api') ? {
|
|
13
13
|
desktop: defaultCollapsed ? 'collapsed' : 'expanded',
|
|
14
14
|
mobile: 'collapsed',
|
|
15
15
|
flyout: 'closed',
|
|
@@ -7,6 +7,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
|
|
|
7
7
|
import { UNSAFE_useMediaQuery } from '@atlaskit/primitives/compiled';
|
|
8
8
|
import { TopNavStartAttachRef } from '../../../context/top-nav-start/top-nav-start-context';
|
|
9
9
|
import { useSideNavVisibility } from '../side-nav/use-side-nav-visibility';
|
|
10
|
+
import { SideNavVisibilityState } from '../side-nav/visibility-context';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
@@ -170,14 +171,22 @@ export function TopNavStart({
|
|
|
170
171
|
|
|
171
172
|
// Used to prevent the reorder animations from running on the initial render.
|
|
172
173
|
const isFirstRenderRef = useRef(true);
|
|
174
|
+
const sideNavState = useContext(SideNavVisibilityState);
|
|
173
175
|
useEffect(() => {
|
|
174
176
|
if (!fg('navx-full-height-sidebar')) {
|
|
175
177
|
return;
|
|
176
178
|
}
|
|
179
|
+
|
|
180
|
+
// Ignore renders until the side nav state is initialized
|
|
181
|
+
// So that apps using the legacy API for setting side nav default state do not see
|
|
182
|
+
// animations when they shouldn't
|
|
183
|
+
if (sideNavState === null) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
177
186
|
if (isFirstRenderRef.current) {
|
|
178
187
|
isFirstRenderRef.current = false;
|
|
179
188
|
}
|
|
180
|
-
}, []);
|
|
189
|
+
}, [sideNavState]);
|
|
181
190
|
|
|
182
191
|
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
183
192
|
// can happen a lot (e.g. if the parent re-renders)
|
|
@@ -28,8 +28,7 @@ export function Root(_ref) {
|
|
|
28
28
|
_ref$skipLinksLabel = _ref.skipLinksLabel,
|
|
29
29
|
skipLinksLabel = _ref$skipLinksLabel === void 0 ? 'Skip to:' : _ref$skipLinksLabel,
|
|
30
30
|
testId = _ref.testId,
|
|
31
|
-
|
|
32
|
-
defaultSideNavCollapsed = _ref$defaultSideNavCo === void 0 ? false : _ref$defaultSideNavCo;
|
|
31
|
+
defaultSideNavCollapsed = _ref.defaultSideNavCollapsed;
|
|
33
32
|
var ref = useRef(null);
|
|
34
33
|
useEffect(function () {
|
|
35
34
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -7,10 +7,11 @@ import * as React from 'react';
|
|
|
7
7
|
import { ax, ix } from "@compiled/react/runtime";
|
|
8
8
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
9
9
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
10
|
-
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
10
|
+
import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
11
11
|
import { bind } from 'bind-event-listener';
|
|
12
12
|
import { flushSync } from 'react-dom';
|
|
13
13
|
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
14
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
14
15
|
import { OpenLayerObserverNamespaceProvider, useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
|
|
15
16
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
16
17
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
@@ -68,6 +69,7 @@ var styles = {
|
|
|
68
69
|
fullHeightSidebar: "_165t56xv _180k1wjm _26vxoned _1mt19dtb"
|
|
69
70
|
};
|
|
70
71
|
var fallbackDefaultWidth = 320;
|
|
72
|
+
export var onPeekStartDelayMs = 500;
|
|
71
73
|
|
|
72
74
|
/**
|
|
73
75
|
* We need an additional component layer so we can wrap the side nav in a `OpenLayerObserver` and have access to the
|
|
@@ -86,6 +88,8 @@ function SideNavInternal(_ref) {
|
|
|
86
88
|
skipLinkLabel = _ref$skipLinkLabel === void 0 ? label : _ref$skipLinkLabel,
|
|
87
89
|
onExpand = _ref.onExpand,
|
|
88
90
|
onCollapse = _ref.onCollapse,
|
|
91
|
+
onPeekStart = _ref.onPeekStart,
|
|
92
|
+
onPeekEnd = _ref.onPeekEnd,
|
|
89
93
|
providedId = _ref.id;
|
|
90
94
|
var id = useLayoutId({
|
|
91
95
|
providedId: providedId
|
|
@@ -152,6 +156,16 @@ function SideNavInternal(_ref) {
|
|
|
152
156
|
type: 'not-active'
|
|
153
157
|
});
|
|
154
158
|
var isFlyoutVisible = (sideNavState === null || sideNavState === void 0 ? void 0 : sideNavState.flyout) === 'open';
|
|
159
|
+
var isExpandedOnDesktopRef = useStableRef(isExpandedOnDesktop);
|
|
160
|
+
var hasPeekStartedRef = useRef(false);
|
|
161
|
+
var onPeekStartRef = useStableRef(onPeekStart);
|
|
162
|
+
var onPeekEndRef = useStableRef(onPeekEnd);
|
|
163
|
+
var onPeekStartTimeoutIdRef = useRef(undefined);
|
|
164
|
+
useEffect(function () {
|
|
165
|
+
return function () {
|
|
166
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
167
|
+
};
|
|
168
|
+
}, []);
|
|
155
169
|
var updateFlyoutState = useMemo(function () {
|
|
156
170
|
function tryAbortPendingClose() {
|
|
157
171
|
if (flyoutStateRef.current.type === 'waiting-for-close') {
|
|
@@ -159,6 +173,7 @@ function SideNavInternal(_ref) {
|
|
|
159
173
|
}
|
|
160
174
|
}
|
|
161
175
|
function open() {
|
|
176
|
+
var prevFlyoutState = flyoutStateRef.current;
|
|
162
177
|
tryAbortPendingClose();
|
|
163
178
|
flyoutStateRef.current = {
|
|
164
179
|
type: 'open'
|
|
@@ -171,8 +186,24 @@ function SideNavInternal(_ref) {
|
|
|
171
186
|
}
|
|
172
187
|
return currentState;
|
|
173
188
|
});
|
|
189
|
+
|
|
190
|
+
// Avoid redundant calls to `onPeekStart()`
|
|
191
|
+
if (prevFlyoutState.type === 'not-active') {
|
|
192
|
+
clearTimeout(onPeekStartTimeoutIdRef.current);
|
|
193
|
+
onPeekStartTimeoutIdRef.current = setTimeout(function () {
|
|
194
|
+
var _onPeekStartRef$curre;
|
|
195
|
+
// If the flyout isn't still open after ~500ms then we won't count the peek
|
|
196
|
+
// As we want to track user intention rather than all hovers
|
|
197
|
+
if (isExpandedOnDesktopRef.current || flyoutStateRef.current.type !== 'open') {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
hasPeekStartedRef.current = true;
|
|
201
|
+
(_onPeekStartRef$curre = onPeekStartRef.current) === null || _onPeekStartRef$curre === void 0 || _onPeekStartRef$curre.call(onPeekStartRef);
|
|
202
|
+
}, onPeekStartDelayMs);
|
|
203
|
+
}
|
|
174
204
|
}
|
|
175
205
|
function close() {
|
|
206
|
+
var prevFlyoutState = flyoutStateRef.current;
|
|
176
207
|
tryAbortPendingClose();
|
|
177
208
|
flyoutStateRef.current = {
|
|
178
209
|
type: 'not-active'
|
|
@@ -185,6 +216,15 @@ function SideNavInternal(_ref) {
|
|
|
185
216
|
}
|
|
186
217
|
return currentState;
|
|
187
218
|
});
|
|
219
|
+
|
|
220
|
+
// Avoid redundant calls to `onPeekEnd()`
|
|
221
|
+
if (prevFlyoutState.type !== 'not-active' && hasPeekStartedRef.current) {
|
|
222
|
+
var _onPeekEndRef$current;
|
|
223
|
+
hasPeekStartedRef.current = false;
|
|
224
|
+
(_onPeekEndRef$current = onPeekEndRef.current) === null || _onPeekEndRef$current === void 0 || _onPeekEndRef$current.call(onPeekEndRef, {
|
|
225
|
+
trigger: isExpandedOnDesktopRef.current ? 'side-nav-expand' : 'mouse-leave'
|
|
226
|
+
});
|
|
227
|
+
}
|
|
188
228
|
}
|
|
189
229
|
return function onAction(action) {
|
|
190
230
|
if (action === 'drag-from-flyout-started') {
|
|
@@ -243,7 +283,7 @@ function SideNavInternal(_ref) {
|
|
|
243
283
|
return;
|
|
244
284
|
}
|
|
245
285
|
};
|
|
246
|
-
}, [openLayerObserver, setSideNavState]);
|
|
286
|
+
}, [isExpandedOnDesktopRef, onPeekEndRef, onPeekStartRef, openLayerObserver, setSideNavState]);
|
|
247
287
|
var toggleVisibilityByScreenResize = useToggleSideNav({
|
|
248
288
|
trigger: 'screen-resize'
|
|
249
289
|
});
|
|
@@ -251,8 +291,8 @@ function SideNavInternal(_ref) {
|
|
|
251
291
|
trigger: 'click-outside-on-mobile'
|
|
252
292
|
});
|
|
253
293
|
useEffect(function () {
|
|
254
|
-
if (fg('
|
|
255
|
-
//
|
|
294
|
+
if (fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
295
|
+
// This is the old version of the hook, so we skip it when the flag is enabled
|
|
256
296
|
return;
|
|
257
297
|
}
|
|
258
298
|
|
|
@@ -265,6 +305,28 @@ function SideNavInternal(_ref) {
|
|
|
265
305
|
lastTrigger: null
|
|
266
306
|
});
|
|
267
307
|
}, [initialDefaultCollapsed, setSideNavState]);
|
|
308
|
+
|
|
309
|
+
// Moving to `useLayoutEffect` so that there's no visual shift in non-SSR environments when using legacy API
|
|
310
|
+
// For SSR the new API is still necessary
|
|
311
|
+
useLayoutEffect(function () {
|
|
312
|
+
if (!fg('platform_dst_nav4_side_nav_default_collapsed_api')) {
|
|
313
|
+
// This is the new version of the hook, so we skip it when the flag is disabled
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (sideNavState !== null) {
|
|
317
|
+
// Only need to do an initial sync if it hasn't been initialized from Root
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Sync the visibility in context (provided in `<Root>`) with the local `defaultCollapsed` prop provided to `SideNav`
|
|
322
|
+
// after SSR hydration. This should only run once, after the initial render on the client.
|
|
323
|
+
setSideNavState({
|
|
324
|
+
desktop: initialDefaultCollapsed ? 'collapsed' : 'expanded',
|
|
325
|
+
mobile: 'collapsed',
|
|
326
|
+
flyout: 'closed',
|
|
327
|
+
lastTrigger: null
|
|
328
|
+
});
|
|
329
|
+
}, [initialDefaultCollapsed, setSideNavState, sideNavState]);
|
|
268
330
|
var handleExpand = useCallback(function (_ref2) {
|
|
269
331
|
var screen = _ref2.screen,
|
|
270
332
|
trigger = _ref2.trigger;
|
|
@@ -672,6 +734,8 @@ export function SideNav(_ref8) {
|
|
|
672
734
|
skipLinkLabel = _ref8$skipLinkLabel === void 0 ? label : _ref8$skipLinkLabel,
|
|
673
735
|
onExpand = _ref8.onExpand,
|
|
674
736
|
onCollapse = _ref8.onCollapse,
|
|
737
|
+
onPeekStart = _ref8.onPeekStart,
|
|
738
|
+
onPeekEnd = _ref8.onPeekEnd,
|
|
675
739
|
id = _ref8.id;
|
|
676
740
|
return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
|
|
677
741
|
namespace: openLayerObserverSideNavNamespace
|
|
@@ -683,6 +747,8 @@ export function SideNav(_ref8) {
|
|
|
683
747
|
skipLinkLabel: skipLinkLabel,
|
|
684
748
|
onExpand: onExpand,
|
|
685
749
|
onCollapse: onCollapse,
|
|
750
|
+
onPeekStart: onPeekStart,
|
|
751
|
+
onPeekEnd: onPeekEnd,
|
|
686
752
|
id: id
|
|
687
753
|
}, children));
|
|
688
754
|
}
|
|
@@ -14,6 +14,7 @@ import { IconButton } from '../../top-nav-items/themed/migration';
|
|
|
14
14
|
import { SideNavToggleButtonAttachRef } from './toggle-button-context';
|
|
15
15
|
import { useSideNavVisibility } from './use-side-nav-visibility';
|
|
16
16
|
import { useToggleSideNav } from './use-toggle-side-nav';
|
|
17
|
+
import { SideNavVisibilityState } from './visibility-context';
|
|
17
18
|
var toggleButtonTooltipOptions = {
|
|
18
19
|
// We're disabling pointer events on the tooltip to prevent it from blocking mouse events, so that the side nav flyout stays open
|
|
19
20
|
// when moving the mouse from the top bar to the side nav.
|
|
@@ -42,10 +43,11 @@ export var SideNavToggleButton = function SideNavToggleButton(_ref) {
|
|
|
42
43
|
}),
|
|
43
44
|
isSideNavExpandedOnDesktop = _useSideNavVisibility.isExpandedOnDesktop,
|
|
44
45
|
isSideNavExpandedOnMobile = _useSideNavVisibility.isExpandedOnMobile;
|
|
46
|
+
var sideNavState = useContext(SideNavVisibilityState);
|
|
45
47
|
|
|
46
|
-
// When `
|
|
47
|
-
//
|
|
48
|
-
var _useState = useState(fg('
|
|
48
|
+
// When default state is provided to `Root` the state in context will already be
|
|
49
|
+
// initialized in SSR
|
|
50
|
+
var _useState = useState(sideNavState === null || !fg('platform_dst_nav4_side_nav_default_collapsed_api') ? !defaultCollapsed : isSideNavExpandedOnDesktop),
|
|
49
51
|
_useState2 = _slicedToArray(_useState, 2),
|
|
50
52
|
isSideNavExpanded = _useState2[0],
|
|
51
53
|
setIsSideNavExpanded = _useState2[1];
|
|
@@ -9,7 +9,7 @@ import { SetSideNavVisibilityState, SideNavVisibilityState } from './visibility-
|
|
|
9
9
|
export var SideNavVisibilityProvider = function SideNavVisibilityProvider(_ref) {
|
|
10
10
|
var children = _ref.children,
|
|
11
11
|
defaultCollapsed = _ref.defaultCollapsed;
|
|
12
|
-
var initialState = fg('
|
|
12
|
+
var initialState = typeof defaultCollapsed === 'boolean' && fg('platform_dst_nav4_side_nav_default_collapsed_api') ? {
|
|
13
13
|
desktop: defaultCollapsed ? 'collapsed' : 'expanded',
|
|
14
14
|
mobile: 'collapsed',
|
|
15
15
|
flyout: 'closed',
|
|
@@ -8,6 +8,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
|
|
|
8
8
|
import { UNSAFE_useMediaQuery } from '@atlaskit/primitives/compiled';
|
|
9
9
|
import { TopNavStartAttachRef } from '../../../context/top-nav-start/top-nav-start-context';
|
|
10
10
|
import { useSideNavVisibility } from '../side-nav/use-side-nav-visibility';
|
|
11
|
+
import { SideNavVisibilityState } from '../side-nav/visibility-context';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
@@ -172,14 +173,22 @@ export function TopNavStart(_ref3) {
|
|
|
172
173
|
|
|
173
174
|
// Used to prevent the reorder animations from running on the initial render.
|
|
174
175
|
var isFirstRenderRef = useRef(true);
|
|
176
|
+
var sideNavState = useContext(SideNavVisibilityState);
|
|
175
177
|
useEffect(function () {
|
|
176
178
|
if (!fg('navx-full-height-sidebar')) {
|
|
177
179
|
return;
|
|
178
180
|
}
|
|
181
|
+
|
|
182
|
+
// Ignore renders until the side nav state is initialized
|
|
183
|
+
// So that apps using the legacy API for setting side nav default state do not see
|
|
184
|
+
// animations when they shouldn't
|
|
185
|
+
if (sideNavState === null) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
179
188
|
if (isFirstRenderRef.current) {
|
|
180
189
|
isFirstRenderRef.current = false;
|
|
181
190
|
}
|
|
182
|
-
}, []);
|
|
191
|
+
}, [sideNavState]);
|
|
183
192
|
|
|
184
193
|
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
185
194
|
// can happen a lot (e.g. if the parent re-renders)
|
|
@@ -44,7 +44,7 @@ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes,
|
|
|
44
44
|
* persisted state using the `onCollapse` and `onExpand` callbacks, to ensure it is up to date
|
|
45
45
|
* when the app is reloaded.
|
|
46
46
|
*
|
|
47
|
-
* __Note:__
|
|
47
|
+
* __Note:__ When provided, the `defaultCollapsed` props on `SideNav` and `SideNavToggleButton` will be ignored.
|
|
48
48
|
*/
|
|
49
49
|
defaultSideNavCollapsed?: boolean;
|
|
50
50
|
}): JSX.Element;
|
|
@@ -15,7 +15,7 @@ type SideNavProps = CommonSlotProps & {
|
|
|
15
15
|
* @deprecated
|
|
16
16
|
*
|
|
17
17
|
* This prop is being replaced by `defaultSideNavCollapsed` on the `Root` element,
|
|
18
|
-
* and will be removed
|
|
18
|
+
* and will be removed in the future.
|
|
19
19
|
*
|
|
20
20
|
* ---
|
|
21
21
|
*
|
|
@@ -53,7 +53,18 @@ type SideNavProps = CommonSlotProps & {
|
|
|
53
53
|
* Note: The trigger parameter is only provided when the `navx-full-height-sidebar` feature flag is enabled.
|
|
54
54
|
*/
|
|
55
55
|
onCollapse?: VisibilityCallback;
|
|
56
|
+
/**
|
|
57
|
+
* Called when the side nav begins peeking / flyout.
|
|
58
|
+
*/
|
|
59
|
+
onPeekStart?: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Called when the side nav stops peeking / flyout.
|
|
62
|
+
*/
|
|
63
|
+
onPeekEnd?: (args: {
|
|
64
|
+
trigger: 'mouse-leave' | 'side-nav-expand';
|
|
65
|
+
}) => void;
|
|
56
66
|
};
|
|
67
|
+
export declare const onPeekStartDelayMs = 500;
|
|
57
68
|
/**
|
|
58
69
|
* The side navigation layout area. It will show on the left (inline start) of the screen.
|
|
59
70
|
*
|
|
@@ -64,5 +75,5 @@ type SideNavProps = CommonSlotProps & {
|
|
|
64
75
|
*/
|
|
65
76
|
export declare function SideNav({ children, defaultCollapsed, defaultWidth, testId, label, // Default value is defined in `SideNavInternal`
|
|
66
77
|
skipLinkLabel, // Default value is defined in `SideNavInternal`
|
|
67
|
-
onExpand, onCollapse, id, }: SideNavProps): JSX.Element;
|
|
78
|
+
onExpand, onCollapse, onPeekStart, onPeekEnd, id, }: SideNavProps): JSX.Element;
|
|
68
79
|
export {};
|
|
@@ -17,7 +17,7 @@ export declare const SideNavToggleButton: ({ defaultCollapsed, expandLabel, coll
|
|
|
17
17
|
* @deprecated
|
|
18
18
|
*
|
|
19
19
|
* This prop is being replaced by `defaultSideNavCollapsed` on the `Root` element,
|
|
20
|
-
* and will be removed
|
|
20
|
+
* and will be removed in the future.
|
|
21
21
|
*
|
|
22
22
|
* ---
|
|
23
23
|
*
|
|
@@ -19,7 +19,7 @@ type TopNavStartProps = {
|
|
|
19
19
|
*
|
|
20
20
|
* You should only render `<SideNavToggleButton>` inside this slot, not as a child.
|
|
21
21
|
*
|
|
22
|
-
* After `
|
|
22
|
+
* After `platform_dst_nav4_side_nav_toggle_button_slot` rolls out,
|
|
23
23
|
* this prop will become required.
|
|
24
24
|
*
|
|
25
25
|
* Consumers that do not need a toggle button can explicitly pass `null`.
|
|
@@ -44,7 +44,7 @@ export declare function Root({ children, xcss, UNSAFE_dangerouslyHoistSlotSizes,
|
|
|
44
44
|
* persisted state using the `onCollapse` and `onExpand` callbacks, to ensure it is up to date
|
|
45
45
|
* when the app is reloaded.
|
|
46
46
|
*
|
|
47
|
-
* __Note:__
|
|
47
|
+
* __Note:__ When provided, the `defaultCollapsed` props on `SideNav` and `SideNavToggleButton` will be ignored.
|
|
48
48
|
*/
|
|
49
49
|
defaultSideNavCollapsed?: boolean;
|
|
50
50
|
}): JSX.Element;
|
|
@@ -15,7 +15,7 @@ type SideNavProps = CommonSlotProps & {
|
|
|
15
15
|
* @deprecated
|
|
16
16
|
*
|
|
17
17
|
* This prop is being replaced by `defaultSideNavCollapsed` on the `Root` element,
|
|
18
|
-
* and will be removed
|
|
18
|
+
* and will be removed in the future.
|
|
19
19
|
*
|
|
20
20
|
* ---
|
|
21
21
|
*
|
|
@@ -53,7 +53,18 @@ type SideNavProps = CommonSlotProps & {
|
|
|
53
53
|
* Note: The trigger parameter is only provided when the `navx-full-height-sidebar` feature flag is enabled.
|
|
54
54
|
*/
|
|
55
55
|
onCollapse?: VisibilityCallback;
|
|
56
|
+
/**
|
|
57
|
+
* Called when the side nav begins peeking / flyout.
|
|
58
|
+
*/
|
|
59
|
+
onPeekStart?: () => void;
|
|
60
|
+
/**
|
|
61
|
+
* Called when the side nav stops peeking / flyout.
|
|
62
|
+
*/
|
|
63
|
+
onPeekEnd?: (args: {
|
|
64
|
+
trigger: 'mouse-leave' | 'side-nav-expand';
|
|
65
|
+
}) => void;
|
|
56
66
|
};
|
|
67
|
+
export declare const onPeekStartDelayMs = 500;
|
|
57
68
|
/**
|
|
58
69
|
* The side navigation layout area. It will show on the left (inline start) of the screen.
|
|
59
70
|
*
|
|
@@ -64,5 +75,5 @@ type SideNavProps = CommonSlotProps & {
|
|
|
64
75
|
*/
|
|
65
76
|
export declare function SideNav({ children, defaultCollapsed, defaultWidth, testId, label, // Default value is defined in `SideNavInternal`
|
|
66
77
|
skipLinkLabel, // Default value is defined in `SideNavInternal`
|
|
67
|
-
onExpand, onCollapse, id, }: SideNavProps): JSX.Element;
|
|
78
|
+
onExpand, onCollapse, onPeekStart, onPeekEnd, id, }: SideNavProps): JSX.Element;
|
|
68
79
|
export {};
|
|
@@ -17,7 +17,7 @@ export declare const SideNavToggleButton: ({ defaultCollapsed, expandLabel, coll
|
|
|
17
17
|
* @deprecated
|
|
18
18
|
*
|
|
19
19
|
* This prop is being replaced by `defaultSideNavCollapsed` on the `Root` element,
|
|
20
|
-
* and will be removed
|
|
20
|
+
* and will be removed in the future.
|
|
21
21
|
*
|
|
22
22
|
* ---
|
|
23
23
|
*
|
|
@@ -19,7 +19,7 @@ type TopNavStartProps = {
|
|
|
19
19
|
*
|
|
20
20
|
* You should only render `<SideNavToggleButton>` inside this slot, not as a child.
|
|
21
21
|
*
|
|
22
|
-
* After `
|
|
22
|
+
* After `platform_dst_nav4_side_nav_toggle_button_slot` rolls out,
|
|
23
23
|
* this prop will become required.
|
|
24
24
|
*
|
|
25
25
|
* Consumers that do not need a toggle button can explicitly pass `null`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/navigation-system",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "The latest navigation system for Atlassian apps.",
|
|
5
5
|
"repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
|
|
6
6
|
"author": "Atlassian Pty Ltd",
|
|
@@ -145,6 +145,9 @@
|
|
|
145
145
|
}
|
|
146
146
|
},
|
|
147
147
|
"platform-feature-flags": {
|
|
148
|
+
"platform_dst_nav4_side_nav_default_collapsed_api": {
|
|
149
|
+
"type": "boolean"
|
|
150
|
+
},
|
|
148
151
|
"platform_dst_nav4_side_nav_toggle_ref_fix": {
|
|
149
152
|
"type": "boolean"
|
|
150
153
|
},
|
|
@@ -170,9 +173,6 @@
|
|
|
170
173
|
"team25-eu-jira-logo-updates-csm-jsm": {
|
|
171
174
|
"type": "boolean"
|
|
172
175
|
},
|
|
173
|
-
"platform_dst_nav4_full_height_sidebar_api_changes": {
|
|
174
|
-
"type": "boolean"
|
|
175
|
-
},
|
|
176
176
|
"platform-dst-tooltip-shortcuts": {
|
|
177
177
|
"type": "boolean"
|
|
178
178
|
},
|