@atlaskit/navigation-system 4.3.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 CHANGED
@@ -1,5 +1,13 @@
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
+
3
11
  ## 4.3.0
4
12
 
5
13
  ### Minor Changes
@@ -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
  });
@@ -703,6 +744,8 @@ function SideNav(_ref8) {
703
744
  skipLinkLabel = _ref8$skipLinkLabel === void 0 ? label : _ref8$skipLinkLabel,
704
745
  onExpand = _ref8.onExpand,
705
746
  onCollapse = _ref8.onCollapse,
747
+ onPeekStart = _ref8.onPeekStart,
748
+ onPeekEnd = _ref8.onPeekEnd,
706
749
  id = _ref8.id;
707
750
  return /*#__PURE__*/React.createElement(_openLayerObserver.OpenLayerObserverNamespaceProvider, {
708
751
  namespace: openLayerObserverSideNavNamespace
@@ -714,6 +757,8 @@ function SideNav(_ref8) {
714
757
  skipLinkLabel: skipLinkLabel,
715
758
  onExpand: onExpand,
716
759
  onCollapse: onCollapse,
760
+ onPeekStart: onPeekStart,
761
+ onPeekEnd: onPeekEnd,
717
762
  id: id
718
763
  }, children));
719
764
  }
@@ -7,6 +7,7 @@ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, u
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
  });
@@ -695,6 +735,8 @@ export function SideNav({
695
735
  // Default value is defined in `SideNavInternal`
696
736
  onExpand,
697
737
  onCollapse,
738
+ onPeekStart,
739
+ onPeekEnd,
698
740
  id
699
741
  }) {
700
742
  return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
@@ -707,6 +749,8 @@ export function SideNav({
707
749
  skipLinkLabel: skipLinkLabel,
708
750
  onExpand: onExpand,
709
751
  onCollapse: onCollapse,
752
+ onPeekStart: onPeekStart,
753
+ onPeekEnd: onPeekEnd,
710
754
  id: id
711
755
  }, children));
712
756
  }
@@ -11,6 +11,7 @@ import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, u
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
  });
@@ -694,6 +734,8 @@ export function SideNav(_ref8) {
694
734
  skipLinkLabel = _ref8$skipLinkLabel === void 0 ? label : _ref8$skipLinkLabel,
695
735
  onExpand = _ref8.onExpand,
696
736
  onCollapse = _ref8.onCollapse,
737
+ onPeekStart = _ref8.onPeekStart,
738
+ onPeekEnd = _ref8.onPeekEnd,
697
739
  id = _ref8.id;
698
740
  return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
699
741
  namespace: openLayerObserverSideNavNamespace
@@ -705,6 +747,8 @@ export function SideNav(_ref8) {
705
747
  skipLinkLabel: skipLinkLabel,
706
748
  onExpand: onExpand,
707
749
  onCollapse: onCollapse,
750
+ onPeekStart: onPeekStart,
751
+ onPeekEnd: onPeekEnd,
708
752
  id: id
709
753
  }, children));
710
754
  }
@@ -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 {};
@@ -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 {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/navigation-system",
3
- "version": "4.3.0",
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",