@atlaskit/navigation-system 2.19.1 → 2.20.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 +11 -0
- package/dist/cjs/ui/page-layout/side-nav/toggle-button-context.js +2 -7
- package/dist/cjs/ui/page-layout/side-nav/toggle-button-provider.js +1 -13
- package/dist/cjs/ui/page-layout/side-nav/toggle-button.compiled.css +1 -2
- package/dist/cjs/ui/page-layout/side-nav/toggle-button.js +1 -18
- package/dist/cjs/ui/page-layout/top-nav/top-nav-start.compiled.css +6 -1
- package/dist/cjs/ui/page-layout/top-nav/top-nav-start.js +120 -8
- package/dist/es2019/ui/page-layout/side-nav/toggle-button-context.js +1 -6
- package/dist/es2019/ui/page-layout/side-nav/toggle-button-provider.js +1 -14
- package/dist/es2019/ui/page-layout/side-nav/toggle-button.compiled.css +1 -2
- package/dist/es2019/ui/page-layout/side-nav/toggle-button.js +2 -19
- package/dist/es2019/ui/page-layout/top-nav/top-nav-start.compiled.css +6 -1
- package/dist/es2019/ui/page-layout/top-nav/top-nav-start.js +117 -8
- package/dist/esm/ui/page-layout/side-nav/toggle-button-context.js +1 -6
- package/dist/esm/ui/page-layout/side-nav/toggle-button-provider.js +1 -13
- package/dist/esm/ui/page-layout/side-nav/toggle-button.compiled.css +1 -2
- package/dist/esm/ui/page-layout/side-nav/toggle-button.js +2 -19
- package/dist/esm/ui/page-layout/top-nav/top-nav-start.compiled.css +6 -1
- package/dist/esm/ui/page-layout/top-nav/top-nav-start.js +120 -8
- package/dist/types/ui/page-layout/side-nav/toggle-button-context.d.ts +0 -4
- package/dist/types/ui/page-layout/side-nav/toggle-button-provider.d.ts +0 -8
- package/dist/types-ts4.5/ui/page-layout/side-nav/toggle-button-context.d.ts +0 -4
- package/dist/types-ts4.5/ui/page-layout/side-nav/toggle-button-provider.d.ts +0 -8
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @atlassian/navigation-system
|
|
2
2
|
|
|
3
|
+
## 2.20.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`bfed073f1849d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/bfed073f1849d) -
|
|
8
|
+
Animations have been added to the `TopNavStart` component, as part of the full height sidebar
|
|
9
|
+
animations. The reorder of `TopNavStart`'s children elements (toggle button, app switcher, app
|
|
10
|
+
logo) when the side nav is toggled (on desktop) will have slide animations.
|
|
11
|
+
|
|
12
|
+
These changes are behind the feature gate `navx-full-height-sidebar`.
|
|
13
|
+
|
|
3
14
|
## 2.19.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.
|
|
7
|
+
exports.SideNavToggleButtonElement = exports.SideNavToggleButtonAttachRef = void 0;
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
|
|
10
10
|
/**
|
|
@@ -23,9 +23,4 @@ var SideNavToggleButtonElement = exports.SideNavToggleButtonElement = /*#__PURE_
|
|
|
23
23
|
* A callback ref is needed because the side nav can be mounted before the elements in the top bar (e.g. if the element
|
|
24
24
|
* is lazy loaded, which happens in Jira and Confluence), which would prevent the event listeners from being set up.
|
|
25
25
|
*/
|
|
26
|
-
var SideNavToggleButtonAttachRef = exports.SideNavToggleButtonAttachRef = /*#__PURE__*/(0, _react.createContext)(_noop.default);
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Used to check if the SideNavToggleButton is rendered inside of its slot in `TopNavStart`.
|
|
30
|
-
*/
|
|
31
|
-
var SideNavToggleButtonSlotContext = exports.SideNavToggleButtonSlotContext = /*#__PURE__*/(0, _react.createContext)(false);
|
|
26
|
+
var SideNavToggleButtonAttachRef = exports.SideNavToggleButtonAttachRef = /*#__PURE__*/(0, _react.createContext)(_noop.default);
|
|
@@ -5,7 +5,7 @@ var _typeof = require("@babel/runtime/helpers/typeof");
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.
|
|
8
|
+
exports.SideNavToggleButtonProvider = void 0;
|
|
9
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
10
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
11
|
var _toggleButtonContext = require("./toggle-button-context");
|
|
@@ -34,16 +34,4 @@ var SideNavToggleButtonProvider = exports.SideNavToggleButtonProvider = function
|
|
|
34
34
|
}, /*#__PURE__*/_react.default.createElement(_toggleButtonContext.SideNavToggleButtonAttachRef.Provider, {
|
|
35
35
|
value: setElement
|
|
36
36
|
}, children));
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Provider for the side nav toggle button slot.
|
|
41
|
-
*
|
|
42
|
-
* This allows us to determine if the toggle button is rendered inside or outside of its slot.
|
|
43
|
-
*/
|
|
44
|
-
var SideNavToggleButtonSlotProvider = exports.SideNavToggleButtonSlotProvider = function SideNavToggleButtonSlotProvider(_ref2) {
|
|
45
|
-
var children = _ref2.children;
|
|
46
|
-
return /*#__PURE__*/_react.default.createElement(_toggleButtonContext.SideNavToggleButtonSlotContext.Provider, {
|
|
47
|
-
value: true
|
|
48
|
-
}, children);
|
|
49
37
|
};
|
|
@@ -32,12 +32,6 @@ var toggleButtonTooltipOptions = {
|
|
|
32
32
|
// For duplicate "mouseenter" issue when changing icons (see below)
|
|
33
33
|
var silentIconStyles = null;
|
|
34
34
|
|
|
35
|
-
/**
|
|
36
|
-
* Wrapper styles to align the toggle button to the end of `TopNavStart`
|
|
37
|
-
* when FHS is expanded.
|
|
38
|
-
*/
|
|
39
|
-
var fullHeightSidebarExpandedWrapperStyles = null;
|
|
40
|
-
|
|
41
35
|
/**
|
|
42
36
|
* __SideNavToggleButton__
|
|
43
37
|
*
|
|
@@ -144,7 +138,7 @@ var SideNavToggleButton = exports.SideNavToggleButton = function SideNavToggleBu
|
|
|
144
138
|
}
|
|
145
139
|
return toggleButtonTooltipOptions;
|
|
146
140
|
}, [shortcut]);
|
|
147
|
-
|
|
141
|
+
return /*#__PURE__*/_react.default.createElement(_migration.IconButton, {
|
|
148
142
|
appearance: "subtle",
|
|
149
143
|
label: isSideNavExpanded ? collapseLabel : expandLabel,
|
|
150
144
|
icon: icon,
|
|
@@ -155,15 +149,4 @@ var SideNavToggleButton = exports.SideNavToggleButton = function SideNavToggleBu
|
|
|
155
149
|
ref: (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_toggle_ref_fix') ? setElement : elementRef,
|
|
156
150
|
tooltip: tooltipProps
|
|
157
151
|
});
|
|
158
|
-
var isInsideSlot = (0, _react.useContext)(_toggleButtonContext.SideNavToggleButtonSlotContext);
|
|
159
|
-
|
|
160
|
-
// Checking `isInsideSlot` in case an app isn't using the slot
|
|
161
|
-
// We don't want to break existing non-slot usage with the left margin
|
|
162
|
-
// This check can be removed in the future, after slot is required for a while.
|
|
163
|
-
if (isInsideSlot && (0, _platformFeatureFlags.fg)('navx-full-height-sidebar')) {
|
|
164
|
-
return /*#__PURE__*/_react.default.createElement("div", {
|
|
165
|
-
className: (0, _runtime.ax)([isSideNavExpandedOnDesktop && "_3l1a1wug"])
|
|
166
|
-
}, iconButton);
|
|
167
|
-
}
|
|
168
|
-
return iconButton;
|
|
169
152
|
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
._zulp1b66{gap:var(--ds-space-050,4px)}
|
|
3
|
-
.
|
|
3
|
+
._zulp1kw7{gap:inherit}
|
|
4
|
+
._yyhykb7n{grid-column:1}._1e0c1kw7{display:inherit}
|
|
5
|
+
._1e0c1txw{display:flex}
|
|
6
|
+
._1ul9idpf{min-width:0}
|
|
4
7
|
._4cvr1h6o{align-items:center}
|
|
5
8
|
._4t3i1osq{height:100%}
|
|
9
|
+
._ahbq1wug{margin-inline-start:auto}
|
|
6
10
|
._bozgutpp{padding-inline-start:var(--ds-space-150,9pt)}
|
|
7
11
|
._lcxv1wug{pointer-events:auto}
|
|
8
12
|
._vchhusvi{box-sizing:border-box}
|
|
13
|
+
@media (prefers-reduced-motion:no-preference){._10t81e03{transition-property:transform}._10t81rjc{transition-property:transform,opacity}._1xq51ytf{transition-timing-function:ease-in-out}._1xq55ucs{transition-timing-function:ease}._mjvc162w{transform:translateX(calc(-2rem + var(--ds-space-050, 4px)*-1))}._mjvcjq3t{transform:translateX(-100%)}._bgpzidpf{opacity:0}._mjvcsws1{transform:translateX(calc(2rem + var(--ds-space-050, 4px)))}._mjvcxwn4{transform:translateX(100%)}._mjvcz12g{transform:translateX(0)}._xrrp188d{transition-duration:.3s}._bgpzkb7n{opacity:1}}
|
|
9
14
|
@media (min-width:64rem){._15rin7od{min-width:unset}._glte1osq{width:100%}._15rip2n4{min-width:330px}._glte1ris{width:max-content}._15ri1mjv{min-width:300px}._1gs5usvi{box-sizing:border-box}._glte93mn{width:var(--n_sNvlw,100%)}._exxmpxbi{padding-inline-end:var(--ds-space-200,1pc)}}
|
|
@@ -11,12 +11,24 @@ require("./top-nav-start.compiled.css");
|
|
|
11
11
|
var _runtime = require("@compiled/react/runtime");
|
|
12
12
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
13
|
var _react = _interopRequireWildcard(require("react"));
|
|
14
|
+
var _useStableRef = _interopRequireDefault(require("@atlaskit/ds-lib/use-stable-ref"));
|
|
14
15
|
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
15
16
|
var _compiled = require("@atlaskit/primitives/compiled");
|
|
16
17
|
var _topNavStartContext = require("../../../context/top-nav-start/top-nav-start-context");
|
|
17
|
-
var _toggleButtonProvider = require("../side-nav/toggle-button-provider");
|
|
18
18
|
var _useSideNavVisibility3 = require("../side-nav/use-side-nav-visibility");
|
|
19
19
|
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
|
+
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
22
|
+
*
|
|
23
|
+
* We are using JS to detect Firefox and disable animations, instead of using CSS, as Compiled currently does not merge duplicate
|
|
24
|
+
* CSS at-rules when at-rules are nested: https://github.com/atlassian-labs/compiled/blob/e04a325915e1d13010205089e4915de0e53bc2d4/packages/css/src/plugins/merge-duplicate-at-rules.ts#L5
|
|
25
|
+
* Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
|
|
26
|
+
*/
|
|
27
|
+
var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
|
28
|
+
|
|
29
|
+
// Placed in a variable, as the value is used in the translateX value for the children wrapper animation.
|
|
30
|
+
var flexGap = "var(--ds-space-050, 4px)";
|
|
31
|
+
|
|
20
32
|
/**
|
|
21
33
|
* Styles for the TopNavStart element.
|
|
22
34
|
*
|
|
@@ -39,10 +51,48 @@ var innerStyles = {
|
|
|
39
51
|
var wrapperStyles = {
|
|
40
52
|
root: "_vchhusvi",
|
|
41
53
|
fullHeightSidebar: "_bozgutpp",
|
|
42
|
-
fullHeightSidebarCollapsed: "_15rip2n4",
|
|
43
54
|
fullHeightSidebarExpanded: "_glte93mn _exxmpxbi"
|
|
44
55
|
};
|
|
45
56
|
|
|
57
|
+
/**
|
|
58
|
+
* We use a fixed translateX offset for the slide animation (used when the TopNavStart children elements are reordered).
|
|
59
|
+
* This fixed offset makes the elements appear to animate smoothly from the old to the new position.
|
|
60
|
+
* This offset is calculated based on:
|
|
61
|
+
* - 32px (2rem) width of the side nav toggle button (IconButton)
|
|
62
|
+
* - 4px gap ('space.050' token) of the flex container
|
|
63
|
+
*
|
|
64
|
+
* The benefit of hardcoding this offset is that we don't need to calculate it using JS each time the sidebar is toggled.
|
|
65
|
+
* However, it could become out of sync if the width of IconButton changes.
|
|
66
|
+
*
|
|
67
|
+
* The alternative is using JS to store the previous position of the children wrapper element, and calculate the offset based on
|
|
68
|
+
* the new position, and then transforming using that offset. This would prevent the animation from going out of sync.
|
|
69
|
+
*/
|
|
70
|
+
var childrenWrapperAnimationOffset = "calc(2rem + ".concat(flexGap, ")");
|
|
71
|
+
var childrenWrapperStyles = {
|
|
72
|
+
root: "_zulp1kw7 _1e0c1kw7 _1ul9idpf",
|
|
73
|
+
animationBaseStyles: "_10t81e03",
|
|
74
|
+
finalPosition: "_mjvcz12g _xrrp188d",
|
|
75
|
+
expandAnimationStartPosition: "_mjvcsws1",
|
|
76
|
+
collapseAnimationStartPosition: "_mjvc162w"
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* We use a fixed translateX offset for the toggle button slide animation (used when the TopNavStart children elements are reordered).
|
|
81
|
+
* The specific value doesn't matter too much for the toggle button animation, so we are using `100%`, which will match the toggle button width.
|
|
82
|
+
*
|
|
83
|
+
* By combining the `translateX` animation with an `opacity` fade, the feel and experience is actually quite similar to animating the
|
|
84
|
+
* element from the exact old position (offset), and it avoids the additional complexity of needing to track and calculate the exact offset.
|
|
85
|
+
*/
|
|
86
|
+
var toggleButtonWrapperStyles = {
|
|
87
|
+
root: "_10t81rjc",
|
|
88
|
+
finalPosition: "_mjvcz12g _xrrp188d _bgpzkb7n",
|
|
89
|
+
expandAnimationStartPosition: "_mjvcjq3t _bgpzidpf",
|
|
90
|
+
collapseAnimationStartPosition: "_mjvcxwn4 _bgpzidpf",
|
|
91
|
+
expandAnimationTimingFunction: "_1xq51ytf",
|
|
92
|
+
collapseAnimationTimingFunction: "_1xq55ucs",
|
|
93
|
+
alignEnd: "_ahbq1wug"
|
|
94
|
+
};
|
|
95
|
+
|
|
46
96
|
/**
|
|
47
97
|
* The consistent key used for the side nav toggle button to ensure it does not get remounted
|
|
48
98
|
* when it is reordered.
|
|
@@ -122,15 +172,77 @@ function TopNavStart(_ref3) {
|
|
|
122
172
|
(0, _compiled.UNSAFE_useMediaQuery)('above.md', function (event) {
|
|
123
173
|
setIsDesktop(event.matches);
|
|
124
174
|
});
|
|
175
|
+
var _useState3 = (0, _react.useState)({
|
|
176
|
+
type: 'idle'
|
|
177
|
+
}),
|
|
178
|
+
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
|
179
|
+
animationState = _useState4[0],
|
|
180
|
+
setAnimationState = _useState4[1];
|
|
181
|
+
|
|
182
|
+
// Used to prevent the reorder animations from running on the initial render.
|
|
183
|
+
var isFirstRenderRef = (0, _react.useRef)(true);
|
|
184
|
+
(0, _react.useEffect)(function () {
|
|
185
|
+
if (!(0, _platformFeatureFlags.fg)('navx-full-height-sidebar')) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (isFirstRenderRef.current) {
|
|
189
|
+
isFirstRenderRef.current = false;
|
|
190
|
+
}
|
|
191
|
+
}, []);
|
|
192
|
+
|
|
193
|
+
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
194
|
+
// can happen a lot (e.g. if the parent re-renders)
|
|
195
|
+
var sideNavToggleButtonStableRef = (0, _useStableRef.default)(sideNavToggleButton);
|
|
196
|
+
(0, _react.useLayoutEffect)(function () {
|
|
197
|
+
if (!(0, _platformFeatureFlags.fg)('navx-full-height-sidebar')) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* This layout effect is used to animate the TopNavStart children elements to their new position after being reordered.
|
|
203
|
+
* It is called when the sidebar's desktop expansion state changes.
|
|
204
|
+
*
|
|
205
|
+
* It works by first setting a translateX offset on the elements, used as the start position of the slide animation.
|
|
206
|
+
* - For the toggle button, it's a fixed offset. It's combined with an opacity, so the exact offset doesn't matter too much.
|
|
207
|
+
* - For the children wrapper (wrapping everything except the toggle button), an offset was chosen to make the animation
|
|
208
|
+
* start position the exact same as the element's old position. See comments for `childrenWrapperStyles` for more details.
|
|
209
|
+
*
|
|
210
|
+
* On the next frame, the translateX offset is cleared, triggering the animation to the new position.
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
if (isFirstRenderRef.current) {
|
|
214
|
+
// No animations on initial render.
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (!sideNavToggleButtonStableRef.current) {
|
|
218
|
+
// If there is no toggle button, there should be no re-order animations.
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Set the translateX offsets so elements are ready to animate to their actual new position after being reordered
|
|
223
|
+
setAnimationState({
|
|
224
|
+
type: isExpandedOnDesktop ? 'expand' : 'collapse'
|
|
225
|
+
});
|
|
226
|
+
requestAnimationFrame(function () {
|
|
227
|
+
// Clear translateX offsets on next frame to trigger animation to new position in a re-render
|
|
228
|
+
setAnimationState({
|
|
229
|
+
type: 'idle'
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// This layout effect is called when the sidebar's desktop expansion state changes.
|
|
234
|
+
}, [isExpandedOnDesktop, sideNavToggleButtonStableRef]);
|
|
125
235
|
var TopNavStartInner = (0, _platformFeatureFlags.fg)('navx-full-height-sidebar') ? TopNavStartInnerFHS : TopNavStartInnerOld;
|
|
126
236
|
return /*#__PURE__*/_react.default.createElement(TopNavStartInner, {
|
|
127
237
|
ref: elementRef,
|
|
128
238
|
testId: testId
|
|
129
|
-
}, !(0, _platformFeatureFlags.fg)('navx-full-height-sidebar') && /*#__PURE__*/_react.default.createElement(
|
|
130
|
-
key: sideNavToggleButtonKey
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
239
|
+
}, !(0, _platformFeatureFlags.fg)('navx-full-height-sidebar') && sideNavToggleButton, sideNavToggleButton && (!isDesktop || !isExpandedOnDesktop) && (0, _platformFeatureFlags.fg)('navx-full-height-sidebar') && /*#__PURE__*/_react.default.createElement("div", {
|
|
240
|
+
key: sideNavToggleButtonKey,
|
|
241
|
+
className: (0, _runtime.ax)([!isFirefox && toggleButtonWrapperStyles.root, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.collapseAnimationTimingFunction, !isFirefox && animationState.type === 'collapse' && toggleButtonWrapperStyles.collapseAnimationStartPosition])
|
|
242
|
+
}, sideNavToggleButton), (0, _platformFeatureFlags.fg)('navx-full-height-sidebar') ? /*#__PURE__*/_react.default.createElement("div", {
|
|
243
|
+
className: (0, _runtime.ax)([childrenWrapperStyles.root, !isFirefox && childrenWrapperStyles.animationBaseStyles, !isFirefox && animationState.type === 'idle' && childrenWrapperStyles.finalPosition, !isFirefox && animationState.type === 'expand' && childrenWrapperStyles.expandAnimationStartPosition, !isFirefox && animationState.type === 'collapse' && childrenWrapperStyles.collapseAnimationStartPosition])
|
|
244
|
+
}, children) : children, sideNavToggleButton && isDesktop && isExpandedOnDesktop && (0, _platformFeatureFlags.fg)('navx-full-height-sidebar') && /*#__PURE__*/_react.default.createElement("div", {
|
|
245
|
+
key: sideNavToggleButtonKey,
|
|
246
|
+
className: (0, _runtime.ax)([!isFirefox && toggleButtonWrapperStyles.root, toggleButtonWrapperStyles.alignEnd, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.expandAnimationTimingFunction, !isFirefox && animationState.type === 'expand' && toggleButtonWrapperStyles.expandAnimationStartPosition])
|
|
135
247
|
}, sideNavToggleButton));
|
|
136
248
|
}
|
|
@@ -16,9 +16,4 @@ export const SideNavToggleButtonElement = /*#__PURE__*/createContext(null);
|
|
|
16
16
|
* A callback ref is needed because the side nav can be mounted before the elements in the top bar (e.g. if the element
|
|
17
17
|
* is lazy loaded, which happens in Jira and Confluence), which would prevent the event listeners from being set up.
|
|
18
18
|
*/
|
|
19
|
-
export const SideNavToggleButtonAttachRef = /*#__PURE__*/createContext(__noop);
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Used to check if the SideNavToggleButton is rendered inside of its slot in `TopNavStart`.
|
|
23
|
-
*/
|
|
24
|
-
export const SideNavToggleButtonSlotContext = /*#__PURE__*/createContext(false);
|
|
19
|
+
export const SideNavToggleButtonAttachRef = /*#__PURE__*/createContext(__noop);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import { SideNavToggleButtonAttachRef, SideNavToggleButtonElement
|
|
2
|
+
import { SideNavToggleButtonAttachRef, SideNavToggleButtonElement } from './toggle-button-context';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Provider for the side nav toggle button contexts.
|
|
@@ -23,17 +23,4 @@ export const SideNavToggleButtonProvider = ({
|
|
|
23
23
|
}, /*#__PURE__*/React.createElement(SideNavToggleButtonAttachRef.Provider, {
|
|
24
24
|
value: setElement
|
|
25
25
|
}, children));
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Provider for the side nav toggle button slot.
|
|
30
|
-
*
|
|
31
|
-
* This allows us to determine if the toggle button is rendered inside or outside of its slot.
|
|
32
|
-
*/
|
|
33
|
-
export const SideNavToggleButtonSlotProvider = ({
|
|
34
|
-
children
|
|
35
|
-
}) => {
|
|
36
|
-
return /*#__PURE__*/React.createElement(SideNavToggleButtonSlotContext.Provider, {
|
|
37
|
-
value: true
|
|
38
|
-
}, children);
|
|
39
26
|
};
|
|
@@ -7,7 +7,7 @@ import SidebarCollapseIcon from '@atlaskit/icon/core/sidebar-collapse';
|
|
|
7
7
|
import SidebarExpandIcon from '@atlaskit/icon/core/sidebar-expand';
|
|
8
8
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
9
9
|
import { IconButton } from '../../top-nav-items/themed/migration';
|
|
10
|
-
import { SideNavToggleButtonAttachRef
|
|
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
13
|
const toggleButtonTooltipOptions = {
|
|
@@ -19,12 +19,6 @@ const toggleButtonTooltipOptions = {
|
|
|
19
19
|
// For duplicate "mouseenter" issue when changing icons (see below)
|
|
20
20
|
const silentIconStyles = null;
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Wrapper styles to align the toggle button to the end of `TopNavStart`
|
|
24
|
-
* when FHS is expanded.
|
|
25
|
-
*/
|
|
26
|
-
const fullHeightSidebarExpandedWrapperStyles = null;
|
|
27
|
-
|
|
28
22
|
/**
|
|
29
23
|
* __SideNavToggleButton__
|
|
30
24
|
*
|
|
@@ -125,7 +119,7 @@ export const SideNavToggleButton = ({
|
|
|
125
119
|
}
|
|
126
120
|
return toggleButtonTooltipOptions;
|
|
127
121
|
}, [shortcut]);
|
|
128
|
-
|
|
122
|
+
return /*#__PURE__*/React.createElement(IconButton, {
|
|
129
123
|
appearance: "subtle",
|
|
130
124
|
label: isSideNavExpanded ? collapseLabel : expandLabel,
|
|
131
125
|
icon: icon,
|
|
@@ -136,15 +130,4 @@ export const SideNavToggleButton = ({
|
|
|
136
130
|
ref: fg('platform_dst_nav4_side_nav_toggle_ref_fix') ? setElement : elementRef,
|
|
137
131
|
tooltip: tooltipProps
|
|
138
132
|
});
|
|
139
|
-
const isInsideSlot = useContext(SideNavToggleButtonSlotContext);
|
|
140
|
-
|
|
141
|
-
// Checking `isInsideSlot` in case an app isn't using the slot
|
|
142
|
-
// We don't want to break existing non-slot usage with the left margin
|
|
143
|
-
// This check can be removed in the future, after slot is required for a while.
|
|
144
|
-
if (isInsideSlot && fg('navx-full-height-sidebar')) {
|
|
145
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
146
|
-
className: ax([isSideNavExpandedOnDesktop && "_3l1a1wug"])
|
|
147
|
-
}, iconButton);
|
|
148
|
-
}
|
|
149
|
-
return iconButton;
|
|
150
133
|
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
._zulp1b66{gap:var(--ds-space-050,4px)}
|
|
3
|
-
.
|
|
3
|
+
._zulp1kw7{gap:inherit}
|
|
4
|
+
._yyhykb7n{grid-column:1}._1e0c1kw7{display:inherit}
|
|
5
|
+
._1e0c1txw{display:flex}
|
|
6
|
+
._1ul9idpf{min-width:0}
|
|
4
7
|
._4cvr1h6o{align-items:center}
|
|
5
8
|
._4t3i1osq{height:100%}
|
|
9
|
+
._ahbq1wug{margin-inline-start:auto}
|
|
6
10
|
._bozgutpp{padding-inline-start:var(--ds-space-150,9pt)}
|
|
7
11
|
._lcxv1wug{pointer-events:auto}
|
|
8
12
|
._vchhusvi{box-sizing:border-box}
|
|
13
|
+
@media (prefers-reduced-motion:no-preference){._10t81e03{transition-property:transform}._10t81rjc{transition-property:transform,opacity}._1xq51ytf{transition-timing-function:ease-in-out}._1xq55ucs{transition-timing-function:ease}._mjvc162w{transform:translateX(calc(-2rem + var(--ds-space-050, 4px)*-1))}._mjvcjq3t{transform:translateX(-100%)}._bgpzidpf{opacity:0}._mjvcsws1{transform:translateX(calc(2rem + var(--ds-space-050, 4px)))}._mjvcxwn4{transform:translateX(100%)}._mjvcz12g{transform:translateX(0)}._xrrp188d{transition-duration:.3s}._bgpzkb7n{opacity:1}}
|
|
9
14
|
@media (min-width:64rem){._15rin7od{min-width:unset}._glte1osq{width:100%}._15rip2n4{min-width:330px}._glte1ris{width:max-content}._15ri1mjv{min-width:300px}._1gs5usvi{box-sizing:border-box}._glte93mn{width:var(--n_sNvlw,100%)}._exxmpxbi{padding-inline-end:var(--ds-space-200,1pc)}}
|
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
import "./top-nav-start.compiled.css";
|
|
3
3
|
import { ax, ix } from "@compiled/react/runtime";
|
|
4
4
|
import React, { forwardRef, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
5
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
5
6
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
6
7
|
import { UNSAFE_useMediaQuery } from '@atlaskit/primitives/compiled';
|
|
7
8
|
import { TopNavStartAttachRef } from '../../../context/top-nav-start/top-nav-start-context';
|
|
8
|
-
import { SideNavToggleButtonSlotProvider } from '../side-nav/toggle-button-provider';
|
|
9
9
|
import { useSideNavVisibility } from '../side-nav/use-side-nav-visibility';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
13
|
+
*
|
|
14
|
+
* We are using JS to detect Firefox and disable animations, instead of using CSS, as Compiled currently does not merge duplicate
|
|
15
|
+
* CSS at-rules when at-rules are nested: https://github.com/atlassian-labs/compiled/blob/e04a325915e1d13010205089e4915de0e53bc2d4/packages/css/src/plugins/merge-duplicate-at-rules.ts#L5
|
|
16
|
+
* Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
|
|
17
|
+
*/
|
|
18
|
+
const isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
|
19
|
+
|
|
20
|
+
// Placed in a variable, as the value is used in the translateX value for the children wrapper animation.
|
|
21
|
+
const flexGap = "var(--ds-space-050, 4px)";
|
|
22
|
+
|
|
11
23
|
/**
|
|
12
24
|
* Styles for the TopNavStart element.
|
|
13
25
|
*
|
|
@@ -30,10 +42,48 @@ const innerStyles = {
|
|
|
30
42
|
const wrapperStyles = {
|
|
31
43
|
root: "_vchhusvi",
|
|
32
44
|
fullHeightSidebar: "_bozgutpp",
|
|
33
|
-
fullHeightSidebarCollapsed: "_15rip2n4",
|
|
34
45
|
fullHeightSidebarExpanded: "_glte93mn _exxmpxbi"
|
|
35
46
|
};
|
|
36
47
|
|
|
48
|
+
/**
|
|
49
|
+
* We use a fixed translateX offset for the slide animation (used when the TopNavStart children elements are reordered).
|
|
50
|
+
* This fixed offset makes the elements appear to animate smoothly from the old to the new position.
|
|
51
|
+
* This offset is calculated based on:
|
|
52
|
+
* - 32px (2rem) width of the side nav toggle button (IconButton)
|
|
53
|
+
* - 4px gap ('space.050' token) of the flex container
|
|
54
|
+
*
|
|
55
|
+
* The benefit of hardcoding this offset is that we don't need to calculate it using JS each time the sidebar is toggled.
|
|
56
|
+
* However, it could become out of sync if the width of IconButton changes.
|
|
57
|
+
*
|
|
58
|
+
* The alternative is using JS to store the previous position of the children wrapper element, and calculate the offset based on
|
|
59
|
+
* the new position, and then transforming using that offset. This would prevent the animation from going out of sync.
|
|
60
|
+
*/
|
|
61
|
+
const childrenWrapperAnimationOffset = `calc(2rem + ${flexGap})`;
|
|
62
|
+
const childrenWrapperStyles = {
|
|
63
|
+
root: "_zulp1kw7 _1e0c1kw7 _1ul9idpf",
|
|
64
|
+
animationBaseStyles: "_10t81e03",
|
|
65
|
+
finalPosition: "_mjvcz12g _xrrp188d",
|
|
66
|
+
expandAnimationStartPosition: "_mjvcsws1",
|
|
67
|
+
collapseAnimationStartPosition: "_mjvc162w"
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* We use a fixed translateX offset for the toggle button slide animation (used when the TopNavStart children elements are reordered).
|
|
72
|
+
* The specific value doesn't matter too much for the toggle button animation, so we are using `100%`, which will match the toggle button width.
|
|
73
|
+
*
|
|
74
|
+
* By combining the `translateX` animation with an `opacity` fade, the feel and experience is actually quite similar to animating the
|
|
75
|
+
* element from the exact old position (offset), and it avoids the additional complexity of needing to track and calculate the exact offset.
|
|
76
|
+
*/
|
|
77
|
+
const toggleButtonWrapperStyles = {
|
|
78
|
+
root: "_10t81rjc",
|
|
79
|
+
finalPosition: "_mjvcz12g _xrrp188d _bgpzkb7n",
|
|
80
|
+
expandAnimationStartPosition: "_mjvcjq3t _bgpzidpf",
|
|
81
|
+
collapseAnimationStartPosition: "_mjvcxwn4 _bgpzidpf",
|
|
82
|
+
expandAnimationTimingFunction: "_1xq51ytf",
|
|
83
|
+
collapseAnimationTimingFunction: "_1xq55ucs",
|
|
84
|
+
alignEnd: "_ahbq1wug"
|
|
85
|
+
};
|
|
86
|
+
|
|
37
87
|
/**
|
|
38
88
|
* The consistent key used for the side nav toggle button to ensure it does not get remounted
|
|
39
89
|
* when it is reordered.
|
|
@@ -115,15 +165,74 @@ export function TopNavStart({
|
|
|
115
165
|
UNSAFE_useMediaQuery('above.md', event => {
|
|
116
166
|
setIsDesktop(event.matches);
|
|
117
167
|
});
|
|
168
|
+
const [animationState, setAnimationState] = useState({
|
|
169
|
+
type: 'idle'
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Used to prevent the reorder animations from running on the initial render.
|
|
173
|
+
const isFirstRenderRef = useRef(true);
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
if (!fg('navx-full-height-sidebar')) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (isFirstRenderRef.current) {
|
|
179
|
+
isFirstRenderRef.current = false;
|
|
180
|
+
}
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
183
|
+
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
184
|
+
// can happen a lot (e.g. if the parent re-renders)
|
|
185
|
+
const sideNavToggleButtonStableRef = useStableRef(sideNavToggleButton);
|
|
186
|
+
useLayoutEffect(() => {
|
|
187
|
+
if (!fg('navx-full-height-sidebar')) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* This layout effect is used to animate the TopNavStart children elements to their new position after being reordered.
|
|
193
|
+
* It is called when the sidebar's desktop expansion state changes.
|
|
194
|
+
*
|
|
195
|
+
* It works by first setting a translateX offset on the elements, used as the start position of the slide animation.
|
|
196
|
+
* - For the toggle button, it's a fixed offset. It's combined with an opacity, so the exact offset doesn't matter too much.
|
|
197
|
+
* - For the children wrapper (wrapping everything except the toggle button), an offset was chosen to make the animation
|
|
198
|
+
* start position the exact same as the element's old position. See comments for `childrenWrapperStyles` for more details.
|
|
199
|
+
*
|
|
200
|
+
* On the next frame, the translateX offset is cleared, triggering the animation to the new position.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
if (isFirstRenderRef.current) {
|
|
204
|
+
// No animations on initial render.
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (!sideNavToggleButtonStableRef.current) {
|
|
208
|
+
// If there is no toggle button, there should be no re-order animations.
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Set the translateX offsets so elements are ready to animate to their actual new position after being reordered
|
|
213
|
+
setAnimationState({
|
|
214
|
+
type: isExpandedOnDesktop ? 'expand' : 'collapse'
|
|
215
|
+
});
|
|
216
|
+
requestAnimationFrame(() => {
|
|
217
|
+
// Clear translateX offsets on next frame to trigger animation to new position in a re-render
|
|
218
|
+
setAnimationState({
|
|
219
|
+
type: 'idle'
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// This layout effect is called when the sidebar's desktop expansion state changes.
|
|
224
|
+
}, [isExpandedOnDesktop, sideNavToggleButtonStableRef]);
|
|
118
225
|
const TopNavStartInner = fg('navx-full-height-sidebar') ? TopNavStartInnerFHS : TopNavStartInnerOld;
|
|
119
226
|
return /*#__PURE__*/React.createElement(TopNavStartInner, {
|
|
120
227
|
ref: elementRef,
|
|
121
228
|
testId: testId
|
|
122
|
-
}, !fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement(
|
|
123
|
-
key: sideNavToggleButtonKey
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
229
|
+
}, !fg('navx-full-height-sidebar') && sideNavToggleButton, sideNavToggleButton && (!isDesktop || !isExpandedOnDesktop) && fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement("div", {
|
|
230
|
+
key: sideNavToggleButtonKey,
|
|
231
|
+
className: ax([!isFirefox && toggleButtonWrapperStyles.root, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.collapseAnimationTimingFunction, !isFirefox && animationState.type === 'collapse' && toggleButtonWrapperStyles.collapseAnimationStartPosition])
|
|
232
|
+
}, sideNavToggleButton), fg('navx-full-height-sidebar') ? /*#__PURE__*/React.createElement("div", {
|
|
233
|
+
className: ax([childrenWrapperStyles.root, !isFirefox && childrenWrapperStyles.animationBaseStyles, !isFirefox && animationState.type === 'idle' && childrenWrapperStyles.finalPosition, !isFirefox && animationState.type === 'expand' && childrenWrapperStyles.expandAnimationStartPosition, !isFirefox && animationState.type === 'collapse' && childrenWrapperStyles.collapseAnimationStartPosition])
|
|
234
|
+
}, children) : children, sideNavToggleButton && isDesktop && isExpandedOnDesktop && fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement("div", {
|
|
235
|
+
key: sideNavToggleButtonKey,
|
|
236
|
+
className: ax([!isFirefox && toggleButtonWrapperStyles.root, toggleButtonWrapperStyles.alignEnd, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.expandAnimationTimingFunction, !isFirefox && animationState.type === 'expand' && toggleButtonWrapperStyles.expandAnimationStartPosition])
|
|
128
237
|
}, sideNavToggleButton));
|
|
129
238
|
}
|
|
@@ -16,9 +16,4 @@ export var SideNavToggleButtonElement = /*#__PURE__*/createContext(null);
|
|
|
16
16
|
* A callback ref is needed because the side nav can be mounted before the elements in the top bar (e.g. if the element
|
|
17
17
|
* is lazy loaded, which happens in Jira and Confluence), which would prevent the event listeners from being set up.
|
|
18
18
|
*/
|
|
19
|
-
export var SideNavToggleButtonAttachRef = /*#__PURE__*/createContext(__noop);
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Used to check if the SideNavToggleButton is rendered inside of its slot in `TopNavStart`.
|
|
23
|
-
*/
|
|
24
|
-
export var SideNavToggleButtonSlotContext = /*#__PURE__*/createContext(false);
|
|
19
|
+
export var SideNavToggleButtonAttachRef = /*#__PURE__*/createContext(__noop);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
2
|
import React, { useState } from 'react';
|
|
3
|
-
import { SideNavToggleButtonAttachRef, SideNavToggleButtonElement
|
|
3
|
+
import { SideNavToggleButtonAttachRef, SideNavToggleButtonElement } from './toggle-button-context';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Provider for the side nav toggle button contexts.
|
|
@@ -26,16 +26,4 @@ export var SideNavToggleButtonProvider = function SideNavToggleButtonProvider(_r
|
|
|
26
26
|
}, /*#__PURE__*/React.createElement(SideNavToggleButtonAttachRef.Provider, {
|
|
27
27
|
value: setElement
|
|
28
28
|
}, children));
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Provider for the side nav toggle button slot.
|
|
33
|
-
*
|
|
34
|
-
* This allows us to determine if the toggle button is rendered inside or outside of its slot.
|
|
35
|
-
*/
|
|
36
|
-
export var SideNavToggleButtonSlotProvider = function SideNavToggleButtonSlotProvider(_ref2) {
|
|
37
|
-
var children = _ref2.children;
|
|
38
|
-
return /*#__PURE__*/React.createElement(SideNavToggleButtonSlotContext.Provider, {
|
|
39
|
-
value: true
|
|
40
|
-
}, children);
|
|
41
29
|
};
|
|
@@ -11,7 +11,7 @@ import SidebarCollapseIcon from '@atlaskit/icon/core/sidebar-collapse';
|
|
|
11
11
|
import SidebarExpandIcon from '@atlaskit/icon/core/sidebar-expand';
|
|
12
12
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
13
13
|
import { IconButton } from '../../top-nav-items/themed/migration';
|
|
14
|
-
import { SideNavToggleButtonAttachRef
|
|
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
17
|
var toggleButtonTooltipOptions = {
|
|
@@ -23,12 +23,6 @@ var toggleButtonTooltipOptions = {
|
|
|
23
23
|
// For duplicate "mouseenter" issue when changing icons (see below)
|
|
24
24
|
var silentIconStyles = null;
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Wrapper styles to align the toggle button to the end of `TopNavStart`
|
|
28
|
-
* when FHS is expanded.
|
|
29
|
-
*/
|
|
30
|
-
var fullHeightSidebarExpandedWrapperStyles = null;
|
|
31
|
-
|
|
32
26
|
/**
|
|
33
27
|
* __SideNavToggleButton__
|
|
34
28
|
*
|
|
@@ -135,7 +129,7 @@ export var SideNavToggleButton = function SideNavToggleButton(_ref) {
|
|
|
135
129
|
}
|
|
136
130
|
return toggleButtonTooltipOptions;
|
|
137
131
|
}, [shortcut]);
|
|
138
|
-
|
|
132
|
+
return /*#__PURE__*/React.createElement(IconButton, {
|
|
139
133
|
appearance: "subtle",
|
|
140
134
|
label: isSideNavExpanded ? collapseLabel : expandLabel,
|
|
141
135
|
icon: icon,
|
|
@@ -146,15 +140,4 @@ export var SideNavToggleButton = function SideNavToggleButton(_ref) {
|
|
|
146
140
|
ref: fg('platform_dst_nav4_side_nav_toggle_ref_fix') ? setElement : elementRef,
|
|
147
141
|
tooltip: tooltipProps
|
|
148
142
|
});
|
|
149
|
-
var isInsideSlot = useContext(SideNavToggleButtonSlotContext);
|
|
150
|
-
|
|
151
|
-
// Checking `isInsideSlot` in case an app isn't using the slot
|
|
152
|
-
// We don't want to break existing non-slot usage with the left margin
|
|
153
|
-
// This check can be removed in the future, after slot is required for a while.
|
|
154
|
-
if (isInsideSlot && fg('navx-full-height-sidebar')) {
|
|
155
|
-
return /*#__PURE__*/React.createElement("div", {
|
|
156
|
-
className: ax([isSideNavExpandedOnDesktop && "_3l1a1wug"])
|
|
157
|
-
}, iconButton);
|
|
158
|
-
}
|
|
159
|
-
return iconButton;
|
|
160
143
|
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
._zulp1b66{gap:var(--ds-space-050,4px)}
|
|
3
|
-
.
|
|
3
|
+
._zulp1kw7{gap:inherit}
|
|
4
|
+
._yyhykb7n{grid-column:1}._1e0c1kw7{display:inherit}
|
|
5
|
+
._1e0c1txw{display:flex}
|
|
6
|
+
._1ul9idpf{min-width:0}
|
|
4
7
|
._4cvr1h6o{align-items:center}
|
|
5
8
|
._4t3i1osq{height:100%}
|
|
9
|
+
._ahbq1wug{margin-inline-start:auto}
|
|
6
10
|
._bozgutpp{padding-inline-start:var(--ds-space-150,9pt)}
|
|
7
11
|
._lcxv1wug{pointer-events:auto}
|
|
8
12
|
._vchhusvi{box-sizing:border-box}
|
|
13
|
+
@media (prefers-reduced-motion:no-preference){._10t81e03{transition-property:transform}._10t81rjc{transition-property:transform,opacity}._1xq51ytf{transition-timing-function:ease-in-out}._1xq55ucs{transition-timing-function:ease}._mjvc162w{transform:translateX(calc(-2rem + var(--ds-space-050, 4px)*-1))}._mjvcjq3t{transform:translateX(-100%)}._bgpzidpf{opacity:0}._mjvcsws1{transform:translateX(calc(2rem + var(--ds-space-050, 4px)))}._mjvcxwn4{transform:translateX(100%)}._mjvcz12g{transform:translateX(0)}._xrrp188d{transition-duration:.3s}._bgpzkb7n{opacity:1}}
|
|
9
14
|
@media (min-width:64rem){._15rin7od{min-width:unset}._glte1osq{width:100%}._15rip2n4{min-width:330px}._glte1ris{width:max-content}._15ri1mjv{min-width:300px}._1gs5usvi{box-sizing:border-box}._glte93mn{width:var(--n_sNvlw,100%)}._exxmpxbi{padding-inline-end:var(--ds-space-200,1pc)}}
|
|
@@ -3,12 +3,24 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
|
3
3
|
import "./top-nav-start.compiled.css";
|
|
4
4
|
import { ax, ix } from "@compiled/react/runtime";
|
|
5
5
|
import React, { forwardRef, useContext, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
6
|
+
import useStableRef from '@atlaskit/ds-lib/use-stable-ref';
|
|
6
7
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { UNSAFE_useMediaQuery } from '@atlaskit/primitives/compiled';
|
|
8
9
|
import { TopNavStartAttachRef } from '../../../context/top-nav-start/top-nav-start-context';
|
|
9
|
-
import { SideNavToggleButtonSlotProvider } from '../side-nav/toggle-button-provider';
|
|
10
10
|
import { useSideNavVisibility } from '../side-nav/use-side-nav-visibility';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Firefox does support these reorder animations, but only partially enabling layout animations would look odd.
|
|
14
|
+
*
|
|
15
|
+
* We are using JS to detect Firefox and disable animations, instead of using CSS, as Compiled currently does not merge duplicate
|
|
16
|
+
* CSS at-rules when at-rules are nested: https://github.com/atlassian-labs/compiled/blob/e04a325915e1d13010205089e4915de0e53bc2d4/packages/css/src/plugins/merge-duplicate-at-rules.ts#L5
|
|
17
|
+
* Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
|
|
18
|
+
*/
|
|
19
|
+
var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
|
|
20
|
+
|
|
21
|
+
// Placed in a variable, as the value is used in the translateX value for the children wrapper animation.
|
|
22
|
+
var flexGap = "var(--ds-space-050, 4px)";
|
|
23
|
+
|
|
12
24
|
/**
|
|
13
25
|
* Styles for the TopNavStart element.
|
|
14
26
|
*
|
|
@@ -31,10 +43,48 @@ var innerStyles = {
|
|
|
31
43
|
var wrapperStyles = {
|
|
32
44
|
root: "_vchhusvi",
|
|
33
45
|
fullHeightSidebar: "_bozgutpp",
|
|
34
|
-
fullHeightSidebarCollapsed: "_15rip2n4",
|
|
35
46
|
fullHeightSidebarExpanded: "_glte93mn _exxmpxbi"
|
|
36
47
|
};
|
|
37
48
|
|
|
49
|
+
/**
|
|
50
|
+
* We use a fixed translateX offset for the slide animation (used when the TopNavStart children elements are reordered).
|
|
51
|
+
* This fixed offset makes the elements appear to animate smoothly from the old to the new position.
|
|
52
|
+
* This offset is calculated based on:
|
|
53
|
+
* - 32px (2rem) width of the side nav toggle button (IconButton)
|
|
54
|
+
* - 4px gap ('space.050' token) of the flex container
|
|
55
|
+
*
|
|
56
|
+
* The benefit of hardcoding this offset is that we don't need to calculate it using JS each time the sidebar is toggled.
|
|
57
|
+
* However, it could become out of sync if the width of IconButton changes.
|
|
58
|
+
*
|
|
59
|
+
* The alternative is using JS to store the previous position of the children wrapper element, and calculate the offset based on
|
|
60
|
+
* the new position, and then transforming using that offset. This would prevent the animation from going out of sync.
|
|
61
|
+
*/
|
|
62
|
+
var childrenWrapperAnimationOffset = "calc(2rem + ".concat(flexGap, ")");
|
|
63
|
+
var childrenWrapperStyles = {
|
|
64
|
+
root: "_zulp1kw7 _1e0c1kw7 _1ul9idpf",
|
|
65
|
+
animationBaseStyles: "_10t81e03",
|
|
66
|
+
finalPosition: "_mjvcz12g _xrrp188d",
|
|
67
|
+
expandAnimationStartPosition: "_mjvcsws1",
|
|
68
|
+
collapseAnimationStartPosition: "_mjvc162w"
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* We use a fixed translateX offset for the toggle button slide animation (used when the TopNavStart children elements are reordered).
|
|
73
|
+
* The specific value doesn't matter too much for the toggle button animation, so we are using `100%`, which will match the toggle button width.
|
|
74
|
+
*
|
|
75
|
+
* By combining the `translateX` animation with an `opacity` fade, the feel and experience is actually quite similar to animating the
|
|
76
|
+
* element from the exact old position (offset), and it avoids the additional complexity of needing to track and calculate the exact offset.
|
|
77
|
+
*/
|
|
78
|
+
var toggleButtonWrapperStyles = {
|
|
79
|
+
root: "_10t81rjc",
|
|
80
|
+
finalPosition: "_mjvcz12g _xrrp188d _bgpzkb7n",
|
|
81
|
+
expandAnimationStartPosition: "_mjvcjq3t _bgpzidpf",
|
|
82
|
+
collapseAnimationStartPosition: "_mjvcxwn4 _bgpzidpf",
|
|
83
|
+
expandAnimationTimingFunction: "_1xq51ytf",
|
|
84
|
+
collapseAnimationTimingFunction: "_1xq55ucs",
|
|
85
|
+
alignEnd: "_ahbq1wug"
|
|
86
|
+
};
|
|
87
|
+
|
|
38
88
|
/**
|
|
39
89
|
* The consistent key used for the side nav toggle button to ensure it does not get remounted
|
|
40
90
|
* when it is reordered.
|
|
@@ -114,15 +164,77 @@ export function TopNavStart(_ref3) {
|
|
|
114
164
|
UNSAFE_useMediaQuery('above.md', function (event) {
|
|
115
165
|
setIsDesktop(event.matches);
|
|
116
166
|
});
|
|
167
|
+
var _useState3 = useState({
|
|
168
|
+
type: 'idle'
|
|
169
|
+
}),
|
|
170
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
171
|
+
animationState = _useState4[0],
|
|
172
|
+
setAnimationState = _useState4[1];
|
|
173
|
+
|
|
174
|
+
// Used to prevent the reorder animations from running on the initial render.
|
|
175
|
+
var isFirstRenderRef = useRef(true);
|
|
176
|
+
useEffect(function () {
|
|
177
|
+
if (!fg('navx-full-height-sidebar')) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (isFirstRenderRef.current) {
|
|
181
|
+
isFirstRenderRef.current = false;
|
|
182
|
+
}
|
|
183
|
+
}, []);
|
|
184
|
+
|
|
185
|
+
// Using a stable ref to avoid re-running the animation layout effect when the toggle button prop value changes, which
|
|
186
|
+
// can happen a lot (e.g. if the parent re-renders)
|
|
187
|
+
var sideNavToggleButtonStableRef = useStableRef(sideNavToggleButton);
|
|
188
|
+
useLayoutEffect(function () {
|
|
189
|
+
if (!fg('navx-full-height-sidebar')) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* This layout effect is used to animate the TopNavStart children elements to their new position after being reordered.
|
|
195
|
+
* It is called when the sidebar's desktop expansion state changes.
|
|
196
|
+
*
|
|
197
|
+
* It works by first setting a translateX offset on the elements, used as the start position of the slide animation.
|
|
198
|
+
* - For the toggle button, it's a fixed offset. It's combined with an opacity, so the exact offset doesn't matter too much.
|
|
199
|
+
* - For the children wrapper (wrapping everything except the toggle button), an offset was chosen to make the animation
|
|
200
|
+
* start position the exact same as the element's old position. See comments for `childrenWrapperStyles` for more details.
|
|
201
|
+
*
|
|
202
|
+
* On the next frame, the translateX offset is cleared, triggering the animation to the new position.
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
if (isFirstRenderRef.current) {
|
|
206
|
+
// No animations on initial render.
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (!sideNavToggleButtonStableRef.current) {
|
|
210
|
+
// If there is no toggle button, there should be no re-order animations.
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Set the translateX offsets so elements are ready to animate to their actual new position after being reordered
|
|
215
|
+
setAnimationState({
|
|
216
|
+
type: isExpandedOnDesktop ? 'expand' : 'collapse'
|
|
217
|
+
});
|
|
218
|
+
requestAnimationFrame(function () {
|
|
219
|
+
// Clear translateX offsets on next frame to trigger animation to new position in a re-render
|
|
220
|
+
setAnimationState({
|
|
221
|
+
type: 'idle'
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// This layout effect is called when the sidebar's desktop expansion state changes.
|
|
226
|
+
}, [isExpandedOnDesktop, sideNavToggleButtonStableRef]);
|
|
117
227
|
var TopNavStartInner = fg('navx-full-height-sidebar') ? TopNavStartInnerFHS : TopNavStartInnerOld;
|
|
118
228
|
return /*#__PURE__*/React.createElement(TopNavStartInner, {
|
|
119
229
|
ref: elementRef,
|
|
120
230
|
testId: testId
|
|
121
|
-
}, !fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement(
|
|
122
|
-
key: sideNavToggleButtonKey
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
231
|
+
}, !fg('navx-full-height-sidebar') && sideNavToggleButton, sideNavToggleButton && (!isDesktop || !isExpandedOnDesktop) && fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement("div", {
|
|
232
|
+
key: sideNavToggleButtonKey,
|
|
233
|
+
className: ax([!isFirefox && toggleButtonWrapperStyles.root, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.collapseAnimationTimingFunction, !isFirefox && animationState.type === 'collapse' && toggleButtonWrapperStyles.collapseAnimationStartPosition])
|
|
234
|
+
}, sideNavToggleButton), fg('navx-full-height-sidebar') ? /*#__PURE__*/React.createElement("div", {
|
|
235
|
+
className: ax([childrenWrapperStyles.root, !isFirefox && childrenWrapperStyles.animationBaseStyles, !isFirefox && animationState.type === 'idle' && childrenWrapperStyles.finalPosition, !isFirefox && animationState.type === 'expand' && childrenWrapperStyles.expandAnimationStartPosition, !isFirefox && animationState.type === 'collapse' && childrenWrapperStyles.collapseAnimationStartPosition])
|
|
236
|
+
}, children) : children, sideNavToggleButton && isDesktop && isExpandedOnDesktop && fg('navx-full-height-sidebar') && /*#__PURE__*/React.createElement("div", {
|
|
237
|
+
key: sideNavToggleButtonKey,
|
|
238
|
+
className: ax([!isFirefox && toggleButtonWrapperStyles.root, toggleButtonWrapperStyles.alignEnd, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.finalPosition, !isFirefox && animationState.type === 'idle' && toggleButtonWrapperStyles.expandAnimationTimingFunction, !isFirefox && animationState.type === 'expand' && toggleButtonWrapperStyles.expandAnimationStartPosition])
|
|
127
239
|
}, sideNavToggleButton));
|
|
128
240
|
}
|
|
@@ -14,7 +14,3 @@ export declare const SideNavToggleButtonElement: import("react").Context<HTMLBut
|
|
|
14
14
|
* is lazy loaded, which happens in Jira and Confluence), which would prevent the event listeners from being set up.
|
|
15
15
|
*/
|
|
16
16
|
export declare const SideNavToggleButtonAttachRef: import("react").Context<(newVal: HTMLButtonElement | null) => void>;
|
|
17
|
-
/**
|
|
18
|
-
* Used to check if the SideNavToggleButton is rendered inside of its slot in `TopNavStart`.
|
|
19
|
-
*/
|
|
20
|
-
export declare const SideNavToggleButtonSlotContext: import("react").Context<boolean>;
|
|
@@ -15,11 +15,3 @@ import React from 'react';
|
|
|
15
15
|
export declare const SideNavToggleButtonProvider: ({ children }: {
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}) => React.JSX.Element;
|
|
18
|
-
/**
|
|
19
|
-
* Provider for the side nav toggle button slot.
|
|
20
|
-
*
|
|
21
|
-
* This allows us to determine if the toggle button is rendered inside or outside of its slot.
|
|
22
|
-
*/
|
|
23
|
-
export declare const SideNavToggleButtonSlotProvider: ({ children }: {
|
|
24
|
-
children: React.ReactNode;
|
|
25
|
-
}) => React.JSX.Element;
|
|
@@ -14,7 +14,3 @@ export declare const SideNavToggleButtonElement: import("react").Context<HTMLBut
|
|
|
14
14
|
* is lazy loaded, which happens in Jira and Confluence), which would prevent the event listeners from being set up.
|
|
15
15
|
*/
|
|
16
16
|
export declare const SideNavToggleButtonAttachRef: import("react").Context<(newVal: HTMLButtonElement | null) => void>;
|
|
17
|
-
/**
|
|
18
|
-
* Used to check if the SideNavToggleButton is rendered inside of its slot in `TopNavStart`.
|
|
19
|
-
*/
|
|
20
|
-
export declare const SideNavToggleButtonSlotContext: import("react").Context<boolean>;
|
|
@@ -15,11 +15,3 @@ import React from 'react';
|
|
|
15
15
|
export declare const SideNavToggleButtonProvider: ({ children }: {
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}) => React.JSX.Element;
|
|
18
|
-
/**
|
|
19
|
-
* Provider for the side nav toggle button slot.
|
|
20
|
-
*
|
|
21
|
-
* This allows us to determine if the toggle button is rendered inside or outside of its slot.
|
|
22
|
-
*/
|
|
23
|
-
export declare const SideNavToggleButtonSlotProvider: ({ children }: {
|
|
24
|
-
children: React.ReactNode;
|
|
25
|
-
}) => React.JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/navigation-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.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",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@atlaskit/analytics-next": "^11.1.0",
|
|
70
|
-
"@atlaskit/avatar": "^25.
|
|
70
|
+
"@atlaskit/avatar": "^25.4.0",
|
|
71
71
|
"@atlaskit/button": "^23.5.0",
|
|
72
72
|
"@atlaskit/css": "^0.14.0",
|
|
73
73
|
"@atlaskit/ds-lib": "^5.1.0",
|