@atlaskit/navigation-system 11.3.2 → 11.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/constellation/layout/images/layout-anatomy-dark.png +0 -0
  3. package/constellation/layout/images/layout-anatomy-light.png +0 -0
  4. package/constellation/layout/images/layout-do-dark.png +0 -0
  5. package/constellation/layout/images/layout-do-light.png +0 -0
  6. package/constellation/layout/images/layout-dont-dark.png +0 -0
  7. package/constellation/layout/images/layout-dont-light.png +0 -0
  8. package/constellation/layout/images/layout-example-dark.png +0 -0
  9. package/constellation/layout/images/layout-example-light.png +0 -0
  10. package/constellation/layout/images/layout-example-sideNav-dark.png +0 -0
  11. package/constellation/layout/images/layout-example-sideNav-light.png +0 -0
  12. package/constellation/layout/images/layout-example-topNav-1-dark.png +0 -0
  13. package/constellation/layout/images/layout-example-topNav-1-light.png +0 -0
  14. package/constellation/layout/images/layout-example-topNav-2-dark.png +0 -0
  15. package/constellation/layout/images/layout-example-topNav-2-light.png +0 -0
  16. package/dist/cjs/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
  17. package/dist/cjs/ui/page-layout/panel.js +10 -3
  18. package/dist/cjs/ui/page-layout/root.js +3 -1
  19. package/dist/cjs/ui/page-layout/side-nav/bind-open-layer-change.js +56 -0
  20. package/dist/cjs/ui/page-layout/side-nav/side-nav.js +166 -56
  21. package/dist/cjs/ui/page-layout/side-nav/toggle-button.js +8 -1
  22. package/dist/cjs/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
  23. package/dist/cjs/ui/page-layout/top-nav/top-nav-end.js +3 -2
  24. package/dist/cjs/ui/page-layout/top-nav/top-nav-middle.js +2 -1
  25. package/dist/es2019/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
  26. package/dist/es2019/ui/page-layout/panel.js +10 -3
  27. package/dist/es2019/ui/page-layout/root.js +3 -1
  28. package/dist/es2019/ui/page-layout/side-nav/bind-open-layer-change.js +51 -0
  29. package/dist/es2019/ui/page-layout/side-nav/side-nav.js +121 -8
  30. package/dist/es2019/ui/page-layout/side-nav/toggle-button.js +8 -1
  31. package/dist/es2019/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
  32. package/dist/es2019/ui/page-layout/top-nav/top-nav-end.js +3 -2
  33. package/dist/es2019/ui/page-layout/top-nav/top-nav-middle.js +2 -1
  34. package/dist/esm/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
  35. package/dist/esm/ui/page-layout/panel.js +10 -3
  36. package/dist/esm/ui/page-layout/root.js +3 -1
  37. package/dist/esm/ui/page-layout/side-nav/bind-open-layer-change.js +50 -0
  38. package/dist/esm/ui/page-layout/side-nav/side-nav.js +166 -56
  39. package/dist/esm/ui/page-layout/side-nav/toggle-button.js +8 -1
  40. package/dist/esm/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
  41. package/dist/esm/ui/page-layout/top-nav/top-nav-end.js +3 -2
  42. package/dist/esm/ui/page-layout/top-nav/top-nav-middle.js +2 -1
  43. package/dist/types/ui/page-layout/side-nav/bind-open-layer-change.d.ts +17 -0
  44. package/package.json +8 -8
@@ -18,6 +18,7 @@ import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/util
18
18
  import { preventUnhandled } from '@atlaskit/pragmatic-drag-and-drop/utils/prevent-unhandled';
19
19
  import Tooltip from '@atlaskit/tooltip/Tooltip';
20
20
  import TooltipContainer from '@atlaskit/tooltip/TooltipContainer';
21
+ import { requestClosePopovers } from '@atlaskit/top-layer/page-surface/request-close-popovers';
21
22
  import VisuallyHidden from '@atlaskit/visually-hidden/visually-hidden';
22
23
  import { contentInsetBlockStart } from '../constants';
23
24
  import { convertResizeBoundToPixels } from './convert-resize-bound-to-pixels';
@@ -159,7 +160,9 @@ const PortaledPanelSplitter = ({
159
160
  min: 200,
160
161
  max: 500
161
162
  });
162
- const openLayerObserver = useOpenLayerObserver();
163
+ const openLayerObserver = useOpenLayerObserver({
164
+ isEnabled: !fg('platform-dst-top-layer')
165
+ });
163
166
 
164
167
  /**
165
168
  * This is a temporary workaround to enable the `SideNavPanelSplitter` component
@@ -250,6 +253,8 @@ const PortaledPanelSplitter = ({
250
253
  onResizeStart === null || onResizeStart === void 0 ? void 0 : onResizeStart(resizeStartData);
251
254
 
252
255
  // Close any open layers when the user starts resizing
256
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
257
+ requestClosePopovers();
253
258
  openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.closeLayers();
254
259
  },
255
260
  onDrag({
@@ -339,6 +344,8 @@ const PortaledPanelSplitter = ({
339
344
  onResizeStart: (...args) => {
340
345
  var _onResizeStartStableR;
341
346
  // Close any open layers when the user starts resizing with keyboard
347
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
348
+ requestClosePopovers();
342
349
  openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.closeLayers();
343
350
  (_onResizeStartStableR = onResizeStartStableRef.current) === null || _onResizeStartStableR === void 0 ? void 0 : _onResizeStartStableR.call(onResizeStartStableRef, ...args);
344
351
  },
@@ -77,6 +77,13 @@ export function Panel({
77
77
  providedId
78
78
  });
79
79
  const isMotionUpliftEnabled = fg('platform-dst-motion-uplift-panel');
80
+ const isContentMotionEnabled = fg('platform-dst-motion-uplift-panel-content');
81
+ const prevContentKeyRef = useRef(contentKey);
82
+ useEffect(() => {
83
+ if (isContentMotionEnabled && contentKey !== undefined) {
84
+ prevContentKeyRef.current = contentKey;
85
+ }
86
+ }, [contentKey, isContentMotionEnabled]);
80
87
  const defaultWidth = useSafeDefaultWidth({
81
88
  defaultWidthProp,
82
89
  fallbackDefaultWidth,
@@ -227,9 +234,9 @@ export function Panel({
227
234
  position: "start"
228
235
  }, /*#__PURE__*/React.createElement("div", {
229
236
  className: ax([styles.scrollContainer, isMotionUpliftEnabled && state === 'entering' && styles.contentEntering, isMotionUpliftEnabled && state === 'exiting' && styles.contentExiting])
230
- }, isMotionUpliftEnabled && contentKey !== undefined ? /*#__PURE__*/React.createElement(ExitingPersistence, {
231
- exitThenEnter: true
237
+ }, isContentMotionEnabled ? /*#__PURE__*/React.createElement(ExitingPersistence, {
238
+ exitThenEnter: contentKey !== undefined
232
239
  }, /*#__PURE__*/React.createElement(PanelContentMotion, {
233
- key: contentKey
240
+ key: contentKey === undefined ? prevContentKeyRef.current : contentKey
234
241
  }, children)) : children)));
235
242
  }
@@ -67,6 +67,8 @@ export function Root({
67
67
  const isChatPanelLayoutEnabled = fg('platform-dst-chat-panel-layout');
68
68
  const LayoutProvider = isChatPanelLayoutEnabled ? LayoutAreaSizingProvider : React.Fragment;
69
69
  const WidthContainer = isChatPanelLayoutEnabled ? LayoutFitContainers : React.Fragment;
70
+ // Top-layer surfaces are found through the DOM, so the observer is only needed when the gate is off.
71
+ const LayerObserverProvider = fg('platform-dst-top-layer') ? React.Fragment : OpenLayerObserver;
70
72
  useEffect(() => {
71
73
  if (process.env.NODE_ENV !== 'production') {
72
74
  const IGNORED_ELEMENTS = ['SCRIPT', 'STYLE'];
@@ -98,7 +100,7 @@ This message will not be displayed in production.
98
100
  isSideNavShortcutEnabled: isSideNavShortcutEnabled
99
101
  }, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(LayoutProvider, isChatPanelLayoutEnabled ? {
100
102
  layoutRef: ref
101
- } : {}, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
103
+ } : {}, /*#__PURE__*/React.createElement(LayerObserverProvider, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
102
104
  value: UNSAFE_dangerouslyHoistSlotSizes
103
105
  }, /*#__PURE__*/React.createElement(SkipLinksProvider, {
104
106
  label: skipLinksLabel,
@@ -0,0 +1,51 @@
1
+ import { bindAll } from 'bind-event-listener';
2
+ /**
3
+ * Calls `onChange` after any popover or dialog inside `within` opens or closes.
4
+ * Read the new state with `hasOpenDialogOrPopover`.
5
+ *
6
+ * `beforetoggle`, `toggle` and `close` do not bubble, so they are observed in the capture phase.
7
+ *
8
+ * `toggle` and `close` are queued tasks. When a surface closes because it unmounts,
9
+ * they fire after the element has left the DOM, so they never reach `within`.
10
+ * `beforetoggle` fires synchronously while the element is still inside `within`,
11
+ * so it also schedules `onChange` in a microtask, after the state has changed.
12
+ */
13
+ export function bindOpenLayerChange({
14
+ within,
15
+ onChange
16
+ }) {
17
+ let isBound = true;
18
+ function listener() {
19
+ onChange();
20
+ }
21
+ function deferredListener() {
22
+ queueMicrotask(() => {
23
+ if (isBound) {
24
+ onChange();
25
+ }
26
+ });
27
+ }
28
+ const unbind = bindAll(within, [{
29
+ type: 'beforetoggle',
30
+ listener: deferredListener,
31
+ options: {
32
+ capture: true
33
+ }
34
+ }, {
35
+ type: 'toggle',
36
+ listener,
37
+ options: {
38
+ capture: true
39
+ }
40
+ }, {
41
+ type: 'close',
42
+ listener,
43
+ options: {
44
+ capture: true
45
+ }
46
+ }]);
47
+ return function cleanup() {
48
+ isBound = false;
49
+ unbind();
50
+ };
51
+ }
@@ -14,6 +14,7 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
14
14
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/adapter/element-adapter';
15
15
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- TODO: migrate to @atlaskit/primitives/compiled
16
16
 
17
+ import { hasOpenDialogOrPopover } from '@atlaskit/top-layer/page-surface/has-open-dialog-or-popover';
17
18
  import { useSkipLinkInternal } from '../../../context/skip-links/use-skip-link-internal';
18
19
  import { TopNavStartElement } from '../../../context/top-nav-start/top-nav-start-element';
19
20
  import { UNSAFE_sideNavLayoutVar, mainMinimumWidthVar, openLayerObserverSideNavNamespace, sideNavLiveWidthVar, sideNavPanelSplitterId, sideNavVar } from '../constants';
@@ -27,6 +28,7 @@ import { useLayoutAreaSizing } from '../use-layout-area-sizing';
27
28
  import { useLayoutId } from '../use-layout-id';
28
29
  import { useResizingWidthCssVarOnRootElement } from '../use-resizing-width-css-var-on-root-element';
29
30
  import { useSafeDefaultWidth } from '../use-safe-default-width';
31
+ import { bindOpenLayerChange } from './bind-open-layer-change';
30
32
  import { sideNavFlyoutCloseDelayMs } from './flyout-close-delay-ms';
31
33
  import { SetSideNavVisibilityState } from './set-side-nav-visibility-state';
32
34
  import { SideNavToggleButtonElement } from './side-nav-toggle-button-element';
@@ -53,6 +55,54 @@ function getPixelCustomProperty(element, propertyName, fallback) {
53
55
  * Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
54
56
  */
55
57
  const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
58
+ const textEntryInputTypes = new Set(['text', 'search', 'email', 'url', 'tel', 'password', 'number']);
59
+ function isEditableContent({
60
+ element
61
+ }) {
62
+ if (element instanceof HTMLElement && typeof element.isContentEditable === 'boolean') {
63
+ return element.isContentEditable;
64
+ }
65
+ // Fallback for DOM implementations without `isContentEditable`.
66
+ const editingHost = element.closest('[contenteditable]');
67
+ return editingHost !== null && editingHost.getAttribute('contenteditable') !== 'false';
68
+ }
69
+
70
+ /**
71
+ * Returns `true` for an element that the user can type text into.
72
+ */
73
+ function isTextEntryElement({
74
+ element
75
+ }) {
76
+ if (element instanceof HTMLInputElement) {
77
+ return textEntryInputTypes.has(element.type) && !element.readOnly && !element.disabled;
78
+ }
79
+ if (element instanceof HTMLTextAreaElement) {
80
+ return !element.readOnly && !element.disabled;
81
+ }
82
+ return isEditableContent({
83
+ element
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Used when the `platform-dst-top-layer` gate is on.
89
+ *
90
+ * The flyout stays open while a layer opened from the side nav is open, or while the user
91
+ * is typing in a text field inside the side nav (for example while renaming an item inline).
92
+ */
93
+ function isFlyoutHeldOpen({
94
+ nav
95
+ }) {
96
+ if (hasOpenDialogOrPopover({
97
+ within: nav
98
+ })) {
99
+ return true;
100
+ }
101
+ const activeElement = nav.ownerDocument.activeElement;
102
+ return activeElement !== null && nav.contains(activeElement) && isTextEntryElement({
103
+ element: activeElement
104
+ });
105
+ }
56
106
  const styles = {
57
107
  root: "_191wglyw _t51zglyw _bfhk1bhr _16qs130s _vchhusvi _4t3ieqxy _152timx3 _kqsw1if8 _1bsb1ego _1pbycs5v _14b5hc79 _qilnk0mc _p5clia51 _4ap3vuon _scbpglyw _89fi1b6c",
58
108
  chatPanelSizing: "_1bsbhc79",
@@ -322,7 +372,9 @@ function SideNavInternal({
322
372
  const toggleButtonElement = useContext(SideNavToggleButtonElement);
323
373
  const topNavStartElement = useContext(TopNavStartElement);
324
374
  const devTimeOnlyAttributes = {};
325
- const openLayerObserver = useOpenLayerObserver();
375
+ const openLayerObserver = useOpenLayerObserver({
376
+ isEnabled: !fg('platform-dst-top-layer')
377
+ });
326
378
  const flyoutStateRef = useRef({
327
379
  type: 'not-active'
328
380
  });
@@ -402,6 +454,17 @@ function SideNavInternal({
402
454
  });
403
455
  }
404
456
  }
457
+ function canClose() {
458
+ if (!fg('platform-dst-top-layer')) {
459
+ return (openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.getCount({
460
+ namespace: openLayerObserverSideNavNamespace
461
+ })) === 0;
462
+ }
463
+ const nav = navRef.current;
464
+ return !(nav && isFlyoutHeldOpen({
465
+ nav
466
+ }));
467
+ }
405
468
  return function onAction(action) {
406
469
  if (action === 'drag-from-flyout-started') {
407
470
  open();
@@ -443,9 +506,7 @@ function SideNavInternal({
443
506
  }
444
507
  if (action === 'ready-to-close') {
445
508
  // If there are no open layers, we can close the flyout.
446
- if ((openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.getCount({
447
- namespace: openLayerObserverSideNavNamespace
448
- })) === 0) {
509
+ if (canClose()) {
449
510
  close();
450
511
  return;
451
512
  }
@@ -787,6 +848,52 @@ function SideNavInternal({
787
848
  namespace: openLayerObserverSideNavNamespace
788
849
  });
789
850
  }, [openLayerObserver, updateFlyoutState]);
851
+ const focusOutTimeoutIdRef = useRef(undefined);
852
+ useEffect(() => {
853
+ // The same as the open layer observer `onChange` above, for when the `platform-dst-top-layer`
854
+ // gate is on. Top-layer surfaces opened from the side nav are DOM descendants of it.
855
+ const nav = navRef.current;
856
+ if (!nav || !fg('platform-dst-top-layer')) {
857
+ return;
858
+ }
859
+ function closeIfReady() {
860
+ if (flyoutStateRef.current.type !== 'ready-to-close' || !nav) {
861
+ return;
862
+ }
863
+ if (isFlyoutHeldOpen({
864
+ nav
865
+ })) {
866
+ return;
867
+ }
868
+ updateFlyoutState('force-close');
869
+ }
870
+ const unbindLayerChange = bindOpenLayerChange({
871
+ within: nav,
872
+ onChange: closeIfReady
873
+ });
874
+ const unbindFocusOut = bind(nav, {
875
+ type: 'focusout',
876
+ listener() {
877
+ // Wait for focus to move before checking where it went.
878
+ clearTimeout(focusOutTimeoutIdRef.current);
879
+ focusOutTimeoutIdRef.current = setTimeout(closeIfReady, 0);
880
+ }
881
+ });
882
+
883
+ // Removing the focused text field (for example when an inline rename ends) fires no
884
+ // `focusout` in some browsers, so also check again when content inside the side nav changes.
885
+ const removalObserver = new MutationObserver(closeIfReady);
886
+ removalObserver.observe(nav, {
887
+ childList: true,
888
+ subtree: true
889
+ });
890
+ return function cleanup() {
891
+ unbindLayerChange();
892
+ unbindFocusOut();
893
+ removalObserver.disconnect();
894
+ clearTimeout(focusOutTimeoutIdRef.current);
895
+ };
896
+ }, [updateFlyoutState]);
790
897
  useEffect(() => {
791
898
  // Clear flyout close timer if being unmounted
792
899
  return function cleanupPendingFlyoutClose() {
@@ -953,9 +1060,7 @@ export function SideNav({
953
1060
  canToggleWithShortcut,
954
1061
  id
955
1062
  }) {
956
- return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
957
- namespace: openLayerObserverSideNavNamespace
958
- }, /*#__PURE__*/React.createElement(SideNavInternal, {
1063
+ const sideNav = /*#__PURE__*/React.createElement(SideNavInternal, {
959
1064
  defaultCollapsed: defaultCollapsed,
960
1065
  defaultWidth: defaultWidth,
961
1066
  minWidth: minWidth,
@@ -969,5 +1074,13 @@ export function SideNav({
969
1074
  onPeekEnd: onPeekEnd,
970
1075
  id: id,
971
1076
  canToggleWithShortcut: canToggleWithShortcut
972
- }, children));
1077
+ }, children);
1078
+
1079
+ // Top-layer surfaces are found through the DOM, so the namespace is only needed when the gate is off.
1080
+ if (fg('platform-dst-top-layer')) {
1081
+ return sideNav;
1082
+ }
1083
+ return /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
1084
+ namespace: openLayerObserverSideNavNamespace
1085
+ }, sideNav);
973
1086
  }
@@ -7,6 +7,8 @@ import { useAnalyticsEvents } from '@atlaskit/analytics-next/useAnalyticsEvents'
7
7
  import SidebarCollapseIcon from '@atlaskit/icon/core/sidebar-collapse';
8
8
  import SidebarExpandIcon from '@atlaskit/icon/core/sidebar-expand';
9
9
  import { useOpenLayerObserver } from '@atlaskit/layering/use-open-layer-observer';
10
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
11
+ import { requestClosePopovers } from '@atlaskit/top-layer/page-surface/request-close-popovers';
10
12
  import { IconButton } from '../../top-nav-items/themed/icon-button';
11
13
  import { SideNavToggleButtonAttachRef } from './side-nav-toggle-button-attach-ref';
12
14
  import { sideNavToggleTooltipKeyboardShortcut } from './side-nav-toggle-tooltip-keyboard-shortcut';
@@ -86,12 +88,17 @@ export const SideNavToggleButton = ({
86
88
  const {
87
89
  createAnalyticsEvent
88
90
  } = useAnalyticsEvents();
89
- const openLayerObserver = useOpenLayerObserver();
91
+ const openLayerObserver = useOpenLayerObserver({
92
+ isEnabled: !fg('platform-dst-top-layer')
93
+ });
90
94
  const handleClick = useCallback((event, analyticsEvent) => {
91
95
  onClick === null || onClick === void 0 ? void 0 : onClick(event, analyticsEvent, {
92
96
  isSideNavVisible: isSideNavExpanded
93
97
  });
94
98
  toggleVisibility();
99
+
100
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
101
+ requestClosePopovers();
95
102
  openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.closeLayers();
96
103
  }, [isSideNavExpanded, onClick, openLayerObserver, toggleVisibility]);
97
104
  const handlePointerEnter = useCallback(() => {
@@ -2,6 +2,9 @@ import { useEffect } from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
3
  import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
4
4
  import { useOpenLayerObserver } from '@atlaskit/layering/use-open-layer-observer';
5
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
6
+ import { hasOpenDialogOrPopover } from '@atlaskit/top-layer/page-surface/has-open-dialog-or-popover';
7
+ import { requestClosePopovers } from '@atlaskit/top-layer/page-surface/request-close-popovers';
5
8
  import { useIsSideNavShortcutEnabled } from './use-is-side-nav-shortcut-enabled';
6
9
  import { useToggleSideNav } from './use-toggle-side-nav';
7
10
 
@@ -11,7 +14,9 @@ import { useToggleSideNav } from './use-toggle-side-nav';
11
14
  export function useSideNavToggleKeyboardShortcut({
12
15
  canToggleWithShortcut
13
16
  }) {
14
- const openLayerObserver = useOpenLayerObserver();
17
+ const openLayerObserver = useOpenLayerObserver({
18
+ isEnabled: !fg('platform-dst-top-layer')
19
+ });
15
20
  const toggleVisibilityByShortcut = useToggleSideNav({
16
21
  trigger: 'keyboard'
17
22
  });
@@ -34,12 +39,21 @@ export function useSideNavToggleKeyboardShortcut({
34
39
  // Ignore repeated keydown events from holding down the keys
35
40
  return;
36
41
  }
42
+ if (fg('platform-dst-top-layer') && hasOpenDialogOrPopover({
43
+ type: 'modal'
44
+ })) {
45
+ // Return early if there are any open modal dialogs
46
+ return;
47
+ }
37
48
  if (openLayerObserver && openLayerObserver.getCount({
38
49
  type: 'modal'
39
50
  }) > 0) {
40
51
  // Return early if there are any open modals
41
52
  return;
42
53
  }
54
+
55
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
56
+ requestClosePopovers();
43
57
  openLayerObserver === null || openLayerObserver === void 0 ? void 0 : openLayerObserver.closeLayers();
44
58
  toggleVisibilityByShortcut();
45
59
  }
@@ -58,7 +58,8 @@ export function TopNavEnd({
58
58
 
59
59
  // The shared provider also covers the mobile overflow popup itself. Keep the
60
60
  // previous content-only namespace coverage when the layout gate is disabled.
61
- const actions = fg('platform-dst-chat-panel-layout') ? children : /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
61
+ // The observer is not used when the top-layer gate is on, so no provider is needed.
62
+ const actions = fg('platform-dst-chat-panel-layout') || fg('platform-dst-top-layer') ? children : /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
62
63
  namespace: openLayerObserverTopNavEndNamespace
63
64
  }, children);
64
65
  const content = /*#__PURE__*/React.createElement("nav", {
@@ -88,7 +89,7 @@ export function TopNavEnd({
88
89
  }) : /*#__PURE__*/React.createElement(List, {
89
90
  xcss: cx(listStyles.root, listStyles.hideOnSmallViewport)
90
91
  }, actions), hasPersistentItems && persistentItems);
91
- return fg('platform-dst-chat-panel-layout') ? /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
92
+ return fg('platform-dst-chat-panel-layout') && !fg('platform-dst-top-layer') ? /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
92
93
  namespace: openLayerObserverTopNavEndNamespace
93
94
  }, content) : content;
94
95
  }
@@ -3,6 +3,7 @@ import "./top-nav-middle.compiled.css";
3
3
  import { ax, ix } from "@compiled/react/runtime";
4
4
  import React from 'react';
5
5
  import { OpenLayerObserverNamespaceProvider } from '@atlaskit/layering/open-layer-observer-namespace-provider';
6
+ import { fg } from '@atlaskit/platform-feature-flags/fg';
6
7
  import { openLayerObserverTopNavMiddleNamespace } from '../constants';
7
8
  const styles = {
8
9
  root: "_zulpu2gc _yyhycs5v _1bsb1osq _4cvr1h6o _1e0c1txw _1bahh9n0 _181n11p5 _1j8b15b0 _lagd1bp4 _1t4c1ris _12e8h9n0"
@@ -21,7 +22,7 @@ export function TopNavMiddle({
21
22
  }) {
22
23
  return /*#__PURE__*/React.createElement("div", {
23
24
  className: ax([styles.root])
24
- }, /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
25
+ }, fg('platform-dst-top-layer') ? children : /*#__PURE__*/React.createElement(OpenLayerObserverNamespaceProvider, {
25
26
  namespace: openLayerObserverTopNavMiddleNamespace
26
27
  }, children));
27
28
  }
@@ -26,6 +26,7 @@ import { disableNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/util
26
26
  import { preventUnhandled } from '@atlaskit/pragmatic-drag-and-drop/utils/prevent-unhandled';
27
27
  import Tooltip from '@atlaskit/tooltip/Tooltip';
28
28
  import TooltipContainer from '@atlaskit/tooltip/TooltipContainer';
29
+ import { requestClosePopovers } from '@atlaskit/top-layer/page-surface/request-close-popovers';
29
30
  import VisuallyHidden from '@atlaskit/visually-hidden/visually-hidden';
30
31
  import { contentInsetBlockStart } from '../constants';
31
32
  import { convertResizeBoundToPixels } from './convert-resize-bound-to-pixels';
@@ -168,7 +169,9 @@ var PortaledPanelSplitter = function PortaledPanelSplitter(_ref3) {
168
169
  _useState4 = _slicedToArray(_useState3, 2),
169
170
  rangeInputBounds = _useState4[0],
170
171
  setRangeInputBounds = _useState4[1];
171
- var openLayerObserver = useOpenLayerObserver();
172
+ var openLayerObserver = useOpenLayerObserver({
173
+ isEnabled: !fg('platform-dst-top-layer')
174
+ });
172
175
 
173
176
  /**
174
177
  * This is a temporary workaround to enable the `SideNavPanelSplitter` component
@@ -259,6 +262,8 @@ var PortaledPanelSplitter = function PortaledPanelSplitter(_ref3) {
259
262
  onResizeStart === null || onResizeStart === void 0 || onResizeStart(resizeStartData);
260
263
 
261
264
  // Close any open layers when the user starts resizing
265
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
266
+ requestClosePopovers();
262
267
  openLayerObserver === null || openLayerObserver === void 0 || openLayerObserver.closeLayers();
263
268
  },
264
269
  onDrag: function onDrag(_ref6) {
@@ -374,6 +379,8 @@ var PortaledPanelSplitter = function PortaledPanelSplitter(_ref3) {
374
379
  onResizeStart: function onResizeStart() {
375
380
  var _onResizeStartStableR;
376
381
  // Close any open layers when the user starts resizing with keyboard
382
+ // Not gated. Top-layer tooltips (`platform-dst-top-layer-tooltip`) are not known to the observer.
383
+ requestClosePopovers();
377
384
  openLayerObserver === null || openLayerObserver === void 0 || openLayerObserver.closeLayers();
378
385
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
379
386
  args[_key] = arguments[_key];
@@ -80,6 +80,13 @@ export function Panel(_ref2) {
80
80
  providedId: providedId
81
81
  });
82
82
  var isMotionUpliftEnabled = fg('platform-dst-motion-uplift-panel');
83
+ var isContentMotionEnabled = fg('platform-dst-motion-uplift-panel-content');
84
+ var prevContentKeyRef = useRef(contentKey);
85
+ useEffect(function () {
86
+ if (isContentMotionEnabled && contentKey !== undefined) {
87
+ prevContentKeyRef.current = contentKey;
88
+ }
89
+ }, [contentKey, isContentMotionEnabled]);
83
90
  var defaultWidth = useSafeDefaultWidth({
84
91
  defaultWidthProp: defaultWidthProp,
85
92
  fallbackDefaultWidth: fallbackDefaultWidth,
@@ -230,9 +237,9 @@ export function Panel(_ref2) {
230
237
  position: "start"
231
238
  }, /*#__PURE__*/React.createElement("div", {
232
239
  className: ax([styles.scrollContainer, isMotionUpliftEnabled && state === 'entering' && styles.contentEntering, isMotionUpliftEnabled && state === 'exiting' && styles.contentExiting])
233
- }, isMotionUpliftEnabled && contentKey !== undefined ? /*#__PURE__*/React.createElement(ExitingPersistence, {
234
- exitThenEnter: true
240
+ }, isContentMotionEnabled ? /*#__PURE__*/React.createElement(ExitingPersistence, {
241
+ exitThenEnter: contentKey !== undefined
235
242
  }, /*#__PURE__*/React.createElement(PanelContentMotion, {
236
- key: contentKey
243
+ key: contentKey === undefined ? prevContentKeyRef.current : contentKey
237
244
  }, children)) : children)));
238
245
  }
@@ -69,6 +69,8 @@ export function Root(_ref2) {
69
69
  var isChatPanelLayoutEnabled = fg('platform-dst-chat-panel-layout');
70
70
  var LayoutProvider = isChatPanelLayoutEnabled ? LayoutAreaSizingProvider : React.Fragment;
71
71
  var WidthContainer = isChatPanelLayoutEnabled ? LayoutFitContainers : React.Fragment;
72
+ // Top-layer surfaces are found through the DOM, so the observer is only needed when the gate is off.
73
+ var LayerObserverProvider = fg('platform-dst-top-layer') ? React.Fragment : OpenLayerObserver;
72
74
  useEffect(function () {
73
75
  if (process.env.NODE_ENV !== 'production') {
74
76
  var IGNORED_ELEMENTS = ['SCRIPT', 'STYLE'];
@@ -91,7 +93,7 @@ export function Root(_ref2) {
91
93
  isSideNavShortcutEnabled: isSideNavShortcutEnabled
92
94
  }, /*#__PURE__*/React.createElement(TopNavStartProvider, null, /*#__PURE__*/React.createElement(LayoutProvider, isChatPanelLayoutEnabled ? {
93
95
  layoutRef: ref
94
- } : {}, /*#__PURE__*/React.createElement(OpenLayerObserver, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
96
+ } : {}, /*#__PURE__*/React.createElement(LayerObserverProvider, null, /*#__PURE__*/React.createElement(DangerouslyHoistSlotSizes.Provider, {
95
97
  value: UNSAFE_dangerouslyHoistSlotSizes
96
98
  }, /*#__PURE__*/React.createElement(SkipLinksProvider, {
97
99
  label: skipLinksLabel,
@@ -0,0 +1,50 @@
1
+ import { bindAll } from 'bind-event-listener';
2
+ /**
3
+ * Calls `onChange` after any popover or dialog inside `within` opens or closes.
4
+ * Read the new state with `hasOpenDialogOrPopover`.
5
+ *
6
+ * `beforetoggle`, `toggle` and `close` do not bubble, so they are observed in the capture phase.
7
+ *
8
+ * `toggle` and `close` are queued tasks. When a surface closes because it unmounts,
9
+ * they fire after the element has left the DOM, so they never reach `within`.
10
+ * `beforetoggle` fires synchronously while the element is still inside `within`,
11
+ * so it also schedules `onChange` in a microtask, after the state has changed.
12
+ */
13
+ export function bindOpenLayerChange(_ref) {
14
+ var within = _ref.within,
15
+ onChange = _ref.onChange;
16
+ var isBound = true;
17
+ function listener() {
18
+ onChange();
19
+ }
20
+ function deferredListener() {
21
+ queueMicrotask(function () {
22
+ if (isBound) {
23
+ onChange();
24
+ }
25
+ });
26
+ }
27
+ var unbind = bindAll(within, [{
28
+ type: 'beforetoggle',
29
+ listener: deferredListener,
30
+ options: {
31
+ capture: true
32
+ }
33
+ }, {
34
+ type: 'toggle',
35
+ listener: listener,
36
+ options: {
37
+ capture: true
38
+ }
39
+ }, {
40
+ type: 'close',
41
+ listener: listener,
42
+ options: {
43
+ capture: true
44
+ }
45
+ }]);
46
+ return function cleanup() {
47
+ isBound = false;
48
+ unbind();
49
+ };
50
+ }