@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.
- package/CHANGELOG.md +20 -0
- package/constellation/layout/images/layout-anatomy-dark.png +0 -0
- package/constellation/layout/images/layout-anatomy-light.png +0 -0
- package/constellation/layout/images/layout-do-dark.png +0 -0
- package/constellation/layout/images/layout-do-light.png +0 -0
- package/constellation/layout/images/layout-dont-dark.png +0 -0
- package/constellation/layout/images/layout-dont-light.png +0 -0
- package/constellation/layout/images/layout-example-dark.png +0 -0
- package/constellation/layout/images/layout-example-light.png +0 -0
- package/constellation/layout/images/layout-example-sideNav-dark.png +0 -0
- package/constellation/layout/images/layout-example-sideNav-light.png +0 -0
- package/constellation/layout/images/layout-example-topNav-1-dark.png +0 -0
- package/constellation/layout/images/layout-example-topNav-1-light.png +0 -0
- package/constellation/layout/images/layout-example-topNav-2-dark.png +0 -0
- package/constellation/layout/images/layout-example-topNav-2-light.png +0 -0
- package/dist/cjs/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
- package/dist/cjs/ui/page-layout/panel.js +10 -3
- package/dist/cjs/ui/page-layout/root.js +3 -1
- package/dist/cjs/ui/page-layout/side-nav/bind-open-layer-change.js +56 -0
- package/dist/cjs/ui/page-layout/side-nav/side-nav.js +166 -56
- package/dist/cjs/ui/page-layout/side-nav/toggle-button.js +8 -1
- package/dist/cjs/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
- package/dist/cjs/ui/page-layout/top-nav/top-nav-end.js +3 -2
- package/dist/cjs/ui/page-layout/top-nav/top-nav-middle.js +2 -1
- package/dist/es2019/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
- package/dist/es2019/ui/page-layout/panel.js +10 -3
- package/dist/es2019/ui/page-layout/root.js +3 -1
- package/dist/es2019/ui/page-layout/side-nav/bind-open-layer-change.js +51 -0
- package/dist/es2019/ui/page-layout/side-nav/side-nav.js +121 -8
- package/dist/es2019/ui/page-layout/side-nav/toggle-button.js +8 -1
- package/dist/es2019/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
- package/dist/es2019/ui/page-layout/top-nav/top-nav-end.js +3 -2
- package/dist/es2019/ui/page-layout/top-nav/top-nav-middle.js +2 -1
- package/dist/esm/ui/page-layout/panel-splitter/panel-splitter.js +8 -1
- package/dist/esm/ui/page-layout/panel.js +10 -3
- package/dist/esm/ui/page-layout/root.js +3 -1
- package/dist/esm/ui/page-layout/side-nav/bind-open-layer-change.js +50 -0
- package/dist/esm/ui/page-layout/side-nav/side-nav.js +166 -56
- package/dist/esm/ui/page-layout/side-nav/toggle-button.js +8 -1
- package/dist/esm/ui/page-layout/side-nav/use-side-nav-toggle-keyboard-shortcut.js +15 -1
- package/dist/esm/ui/page-layout/top-nav/top-nav-end.js +3 -2
- package/dist/esm/ui/page-layout/top-nav/top-nav-middle.js +2 -1
- package/dist/types/ui/page-layout/side-nav/bind-open-layer-change.d.ts +17 -0
- 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
|
-
},
|
|
231
|
-
exitThenEnter:
|
|
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(
|
|
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 ((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
234
|
-
exitThenEnter:
|
|
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(
|
|
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
|
+
}
|