@atlaskit/navigation-system 5.32.2 → 5.33.1

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 (47) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/constellation/layout/diagram.tsx +1 -1
  3. package/dist/cjs/ui/page-layout/banner.compiled.css +1 -1
  4. package/dist/cjs/ui/page-layout/banner.js +1 -1
  5. package/dist/cjs/ui/page-layout/constants.js +7 -6
  6. package/dist/cjs/ui/page-layout/panel-splitter/panel-splitter.compiled.css +1 -0
  7. package/dist/cjs/ui/page-layout/panel-splitter/panel-splitter.js +13 -5
  8. package/dist/cjs/ui/page-layout/panel-splitter/side-nav-panel-splitter.js +52 -12
  9. package/dist/cjs/ui/page-layout/side-nav/side-nav.compiled.css +4 -3
  10. package/dist/cjs/ui/page-layout/side-nav/side-nav.js +25 -20
  11. package/dist/cjs/ui/page-layout/top-nav/top-nav.compiled.css +0 -1
  12. package/dist/cjs/ui/page-layout/top-nav/top-nav.js +2 -16
  13. package/dist/cjs/ui/top-nav-items/search.js +0 -1
  14. package/dist/es2019/ui/page-layout/banner.compiled.css +1 -1
  15. package/dist/es2019/ui/page-layout/banner.js +1 -1
  16. package/dist/es2019/ui/page-layout/constants.js +7 -6
  17. package/dist/es2019/ui/page-layout/panel-splitter/panel-splitter.compiled.css +1 -0
  18. package/dist/es2019/ui/page-layout/panel-splitter/panel-splitter.js +13 -5
  19. package/dist/es2019/ui/page-layout/panel-splitter/side-nav-panel-splitter.js +43 -13
  20. package/dist/es2019/ui/page-layout/side-nav/side-nav.compiled.css +4 -3
  21. package/dist/es2019/ui/page-layout/side-nav/side-nav.js +17 -6
  22. package/dist/es2019/ui/page-layout/top-nav/top-nav.compiled.css +0 -1
  23. package/dist/es2019/ui/page-layout/top-nav/top-nav.js +3 -18
  24. package/dist/es2019/ui/top-nav-items/search.js +0 -1
  25. package/dist/esm/ui/page-layout/banner.compiled.css +1 -1
  26. package/dist/esm/ui/page-layout/banner.js +1 -1
  27. package/dist/esm/ui/page-layout/constants.js +7 -6
  28. package/dist/esm/ui/page-layout/panel-splitter/panel-splitter.compiled.css +1 -0
  29. package/dist/esm/ui/page-layout/panel-splitter/panel-splitter.js +13 -5
  30. package/dist/esm/ui/page-layout/panel-splitter/side-nav-panel-splitter.js +53 -13
  31. package/dist/esm/ui/page-layout/side-nav/side-nav.compiled.css +4 -3
  32. package/dist/esm/ui/page-layout/side-nav/side-nav.js +26 -21
  33. package/dist/esm/ui/page-layout/top-nav/top-nav.compiled.css +0 -1
  34. package/dist/esm/ui/page-layout/top-nav/top-nav.js +3 -18
  35. package/dist/esm/ui/top-nav-items/search.js +0 -1
  36. package/dist/types/components/badge-container.d.ts +1 -1
  37. package/dist/types/ui/page-layout/constants.d.ts +1 -1
  38. package/dist/types/ui/top-nav-items/search.d.ts +11 -9
  39. package/dist/types-ts4.5/components/badge-container.d.ts +1 -1
  40. package/dist/types-ts4.5/ui/page-layout/constants.d.ts +1 -1
  41. package/dist/types-ts4.5/ui/top-nav-items/search.d.ts +11 -9
  42. package/package.json +8 -7
  43. package/dist/cjs/ui/page-layout/use-open-layer-count.js +0 -59
  44. package/dist/es2019/ui/page-layout/use-open-layer-count.js +0 -44
  45. package/dist/esm/ui/page-layout/use-open-layer-count.js +0 -53
  46. package/dist/types/ui/page-layout/use-open-layer-count.d.ts +0 -8
  47. package/dist/types-ts4.5/ui/page-layout/use-open-layer-count.d.ts +0 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @atlassian/navigation-system
2
2
 
3
+ ## 5.33.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`790a15b2d6cc0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/790a15b2d6cc0) -
8
+ Fixes a bug behind the `platform_dst_nav4_side_nav_resize_tooltip_feedback` gate, where the side
9
+ nav panel splitter tooltip could be mispositioned on scaled displays.
10
+ - Updated dependencies
11
+
12
+ ## 5.33.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`57838cec99f29`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/57838cec99f29) -
17
+ Layering refactors have been made to the top nav and full height sidebar, behind the feature gate
18
+ `platform-dst-side-nav-layering-fixes`.
19
+ - Layers inside the side nav that are rendered to parent (`shouldRenderToParent`) will be layered
20
+ below the top nav and banner.
21
+ - Refactors have been made to the positioning and render location of the side nav panel splitter.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies
26
+
3
27
  ## 5.32.2
4
28
 
5
29
  ### Patch Changes
@@ -91,7 +91,7 @@ const styles = cssMap({
91
91
  },
92
92
  });
93
93
 
94
- export const PageLayoutDiagram = () => (
94
+ export const PageLayoutDiagram: () => JSX.Element = () => (
95
95
  <div css={styles.root}>
96
96
  <div css={[styles.slot, styles.bannerSlot]}>
97
97
  <div css={styles.placeholder}>Banner</div>
@@ -1,7 +1,7 @@
1
1
 
2
2
  ._nd5ldkfm{grid-area:banner}._152tidpf{inset-block-start:0}
3
3
  ._18m915vq{overflow-y:hidden}
4
- ._1pbycs5v{z-index:2}
4
+ ._1pby11wp{z-index:3}
5
5
  ._1pbyegat{z-index:4}
6
6
  ._1reo15vq{overflow-x:hidden}
7
7
  ._4t3iutvi{height:var(--n_bnrM)}
@@ -20,7 +20,7 @@ var _idUtils = require("./id-utils");
20
20
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
21
21
  var styles = {
22
22
  root: "_nd5ldkfm _1reo15vq _18m915vq _4t3iutvi _152tidpf _kqsw1if8 _1pbyegat",
23
- fullHeightSidebarWithLayeringFixes: "_1pbycs5v"
23
+ fullHeightSidebarWithLayeringFixes: "_1pby11wp"
24
24
  };
25
25
 
26
26
  /**
@@ -54,14 +54,15 @@ var UNSAFE_MAIN_INLINE_END_FOR_LEGACY_PAGES_ONLY = exports.UNSAFE_MAIN_INLINE_EN
54
54
  * rely on accessing them through global means.
55
55
  */
56
56
  var localSlotLayers = exports.localSlotLayers = {
57
+ // The side nav panel splitter is layered above the top nav when FHS and 'platform-dst-side-nav-layering-fixes' is enabled.
58
+ // It has the same z-index value, but is rendered after the top nav in the DOM so is stacked above.
59
+ sideNavPanelSplitterFHS: 4,
57
60
  topBar: 4,
58
61
  banner: 4,
59
- // With the FHS layering refactors, the banner and top nav have a lower z-index to allow layers from the side nav to overlay them.
60
- // When they all have equal z-index values, the DOM order determines the layering - meaning the side nav will be layered above the rest.
61
- // But, when the top bar contains an open layer, it needs to be layered above the side nav, so has a higher value.
62
- topNavFHSWithOpenLayer: 3,
63
- bannerFHS: 2,
64
- topNavFHS: 2,
62
+ // When FHS and 'platform-dst-side-nav-layering-fixes' is enabled, the side nav is layered below the top nav,
63
+ // but above the panel
64
+ bannerFHS: 3,
65
+ topNavFHS: 3,
65
66
  sideNav: 2,
66
67
  panelSmallViewports: 1
67
68
  };
@@ -2,6 +2,7 @@
2
2
  ._12y31o36{outline-width:medium}
3
3
  ._152tidpf{inset-block-start:0}
4
4
  ._1bsb1l7b{width:3px}
5
+ ._1bsb1ris{width:max-content}
5
6
  ._1bsbl52n{width:17px}
6
7
  ._1e021v6z{inset-inline-start:7px}
7
8
  ._1e02zeo2{inset-inline-start:-9px}
@@ -58,7 +58,8 @@ var lineStyles = {
58
58
  root: "_kqswstnw _1e0c1ule _1bsb1l7b _4t3i1osq _syaz1kw7 _bfhk1r31 _1e021v6z"
59
59
  };
60
60
  var tooltipStyles = {
61
- root: "_ahbq196n"
61
+ root: "_ahbq196n",
62
+ fullHeightSidebarWithLayeringFixes: "_1bsb1ris"
62
63
  };
63
64
  var panelSplitterDragDataSymbol = Symbol('panel-splitter-drag-data');
64
65
  function signPanelSplitterDragData(data) {
@@ -72,7 +73,11 @@ var PanelSplitterTooltip = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, r
72
73
  if (!props.style || !props.style.transform) {
73
74
  return props.style;
74
75
  }
75
- var _props$style$transfor = props.style.transform.matchAll(/\d+px/g),
76
+
77
+ // We cannot just match against integers safely, as browsers may introduce non-integer values due to scaling
78
+ // In the future we can probably use `CSSStyleValue.parse()` to get the browser to handle parsing for us,
79
+ // but there's no Firefox support yet.
80
+ var _props$style$transfor = props.style.transform.matchAll(/(?:-|\+)?\d*\.?\d+px/g),
76
81
  _props$style$transfor2 = (0, _slicedToArray2.default)(_props$style$transfor, 2),
77
82
  translateX = _props$style$transfor2[0],
78
83
  translateY = _props$style$transfor2[1];
@@ -96,7 +101,7 @@ var PanelSplitterTooltip = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, r
96
101
  // Must be statically passed
97
102
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/design-system/no-unsafe-style-overrides
98
103
  ,
99
- className: (0, _runtime.ax)([tooltipStyles.root, className])
104
+ className: (0, _runtime.ax)([tooltipStyles.root, (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && tooltipStyles.fullHeightSidebarWithLayeringFixes, className])
100
105
  // eslint-disable-next-line @atlaskit/design-system/no-unsafe-style-overrides
101
106
  ,
102
107
 
@@ -111,10 +116,12 @@ var PanelSplitterTooltip = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, r
111
116
  var MaybeTooltip = function MaybeTooltip(_ref2) {
112
117
  var tooltipContent = _ref2.tooltipContent,
113
118
  shortcut = _ref2.shortcut,
114
- children = _ref2.children;
119
+ children = _ref2.children,
120
+ testId = _ref2.testId;
115
121
  var isFhsEnabled = (0, _useIsFhsEnabled.useIsFhsEnabled)();
116
122
  if (tooltipContent && isFhsEnabled) {
117
123
  return /*#__PURE__*/React.createElement(_tooltip.default, {
124
+ testId: testId,
118
125
  content: tooltipContent,
119
126
  shortcut: shortcut,
120
127
  position: (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_resize_tooltip_feedback') ? 'mouse-y' : 'mouse',
@@ -399,7 +406,8 @@ var PortaledPanelSplitter = function PortaledPanelSplitter(_ref3) {
399
406
  className: (0, _runtime.ax)([containerStyles.root, position === 'start' && containerStyles.positionStart, position === 'end' && containerStyles.positionEnd])
400
407
  }, /*#__PURE__*/React.createElement(MaybeTooltip, {
401
408
  tooltipContent: tooltipContent,
402
- shortcut: shortcut
409
+ shortcut: shortcut,
410
+ testId: testId && (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_resize_tooltip_feedback') ? "".concat(testId, "-tooltip") : undefined
403
411
  }, /*#__PURE__*/React.createElement("div", {
404
412
  ref: splitterRef,
405
413
  "data-testid": testId,
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
8
  exports.SideNavPanelSplitter = void 0;
9
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
10
  var _react = _interopRequireWildcard(require("react"));
10
11
  var _tinyInvariant = _interopRequireDefault(require("tiny-invariant"));
12
+ var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
11
13
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
14
  var _useIsFhsEnabled = require("../../fhs-rollout/use-is-fhs-enabled");
13
15
  var _constants = require("../constants");
14
16
  var _useToggleSideNav = require("../side-nav/use-toggle-side-nav");
15
- var _useOpenLayerCount = require("../use-open-layer-count");
16
17
  var _context = require("./context");
17
18
  var _panelSplitter = require("./panel-splitter");
18
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); }
@@ -22,14 +23,56 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
22
23
  * Placed outside the component for stability, as the list is used as an effect dependency.
23
24
  */
24
25
  var openLayerNamespacesToCheck = [
25
- // We don't technically need to check the side nav for open layers, as they wouldn't overlay the
26
- // panel splitter, as it sits within the same stacking context as the side nav. For consistency however,
27
- // we check it as well.
26
+ // The side nav panel splitter is layered above the side nav, so needs to be disabled when there are open popups in the side nav.
28
27
  _constants.openLayerObserverSideNavNamespace,
29
- // When there is an open layer in the top nav, the top nav is given a higher z-index than the side nav.
30
- // This means the part of the side nav panel splitter that was sitting above the top nav will no longer
31
- // be interactive (as it is now behind the top nav). So, we need to disable the entire panel splitter.
28
+ // The side nav panel splitter is layered above the top nav, so needs to be disabled when there are open popups in the top nav.
32
29
  _constants.openLayerObserverTopNavStartNamespace, _constants.openLayerObserverTopNavMiddleNamespace, _constants.openLayerObserverTopNavEndNamespace];
30
+
31
+ /**
32
+ * Returns whether there are any open popups in the side nav or top nav
33
+ */
34
+ function useHasOpenPopupsInSideNavOrTopNav() {
35
+ var isFhsEnabled = (0, _useIsFhsEnabled.useIsFhsEnabled)();
36
+ var openLayerObserver = (0, _openLayerObserver.useOpenLayerObserver)();
37
+ // Setting the initial state to false, as it is unlikely that there would be any open popups when the app starts.
38
+ var _useState = (0, _react.useState)(false),
39
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
40
+ hasOpenPopups = _useState2[0],
41
+ setHasOpenPopups = _useState2[1];
42
+ (0, _react.useEffect)(function () {
43
+ if (!openLayerObserver || !isFhsEnabled || !(0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes')) {
44
+ return;
45
+ }
46
+ function updateState() {
47
+ if (!openLayerObserver) {
48
+ return;
49
+ }
50
+ var hasAnyOpenLayers = openLayerNamespacesToCheck.some(function (namespace) {
51
+ return openLayerObserver.getCount({
52
+ namespace: namespace,
53
+ type: 'popup'
54
+ }) > 0;
55
+ });
56
+ setHasOpenPopups(hasAnyOpenLayers);
57
+ }
58
+
59
+ // Initial check. We do this _in case_ a popup is already open when the component mounts.
60
+ updateState();
61
+
62
+ // Subscribe to each namespace
63
+ var cleanups = openLayerNamespacesToCheck.map(function (namespace) {
64
+ return openLayerObserver.onChange(updateState, {
65
+ namespace: namespace
66
+ });
67
+ });
68
+ return function cleanupHook() {
69
+ cleanups.forEach(function (cleanup) {
70
+ return cleanup();
71
+ });
72
+ };
73
+ }, [isFhsEnabled, openLayerObserver]);
74
+ return hasOpenPopups;
75
+ }
33
76
  /**
34
77
  * _SideNavPanelSplitter_
35
78
  *
@@ -61,11 +104,8 @@ var SideNavPanelSplitter = exports.SideNavPanelSplitter = function SideNavPanelS
61
104
  // The logic and state for disabling the panel splitter when there are open popups
62
105
  // in the side nav or top nav is being placed here, instead of in `SideNav`, to prevent
63
106
  // re-rendering the side nav anytime the number of open popups changes.
64
- var hasOpenLayers = (0, _useOpenLayerCount.useHasOpenLayers)({
65
- namespaces: openLayerNamespacesToCheck,
66
- type: 'popup'
67
- });
68
- if (hasOpenLayers && isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes')) {
107
+ var hasOpenLayersInSideNavOrTopNav = useHasOpenPopupsInSideNavOrTopNav();
108
+ if (hasOpenLayersInSideNavOrTopNav && isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes')) {
69
109
  return null;
70
110
  }
71
111
  return /*#__PURE__*/_react.default.createElement(_context.OnDoubleClickContext.Provider, {
@@ -9,19 +9,20 @@
9
9
  ._1e0c1txw{display:flex}
10
10
  ._1e0cglyw{display:none}
11
11
  ._1pbycs5v{z-index:2}
12
+ ._1pbyegat{z-index:4}
12
13
  ._2lx21bp4{flex-direction:column}
13
14
  ._4t3i1osq{height:100%}
14
15
  ._4t3ieqxy{height:calc(100vh - var(--n_bnrM, 0px) - var(--n_tNvM, 0px))}
15
16
  ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
16
17
  ._kqsw1if8{position:sticky}
17
- ._kqswstnw{position:absolute}
18
- ._rjxpidpf{inset-inline-end:0}
18
+ ._kqsw1n9t{position:fixed}
19
+ ._t9ectl5p{transform:translateX(calc(var(--n_snvRsz, var(--n_snvW, 0px))))}
19
20
  ._u7coidpf{inset-block-end:0}
20
21
  ._vchhusvi{box-sizing:border-box}
21
22
  [dir=rtl] ._65m41mrw{--animation-direction:-1}
22
23
  @media (prefers-reduced-motion:no-preference){._10t88iot{transition-property:transform,display}._1vrh1a5r{transition-behavior:allow-discrete}._xrrpfnf5{transition-duration:.2s}._1lh81gzg{grid-area:main}._1xq51mm8{transition-timing-function:cubic-bezier(0,.4,0,1)}@starting-style{._1nu51p9u{transform:translateX(calc(-100%*var(--animation-direction)))}}._1xq51ku9{transition-timing-function:cubic-bezier(.6,0,0,1)}}
23
24
  @media (min-width:48rem){._14b5hc79{width:var(--n_snvRsz,var(--n_sNvw))}}
24
- @media (min-width:64rem){._165t56xv{height:calc(100vh - var(--n_bnrM, 0px))}._180k1wjm{inset-block-start:calc(var(--n_bnrM, 0px))}._26vxoned{padding-block-start:calc(var(--n_tNvM, 0px))}._1mt19dtb{margin-block-start:calc(var(--n_tNvM, 0px)*-1)}._dm2518uv{display:initial}._dm25glyw{display:none}._qiln1gzg{grid-area:main}._p5clglyw{border-inline-end:none}._4ap31bhr{background-color:var(--ds-surface-overlay,#fff)}._scbp130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}._qilnk0mc{grid-area:side-nav}._p5clia51{border-inline-end:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}._4ap3vuon{background-color:var(--ds-surface,#fff)}._scbpglyw{box-shadow:none}}
25
+ @media (min-width:64rem){._165t56xv{height:calc(100vh - var(--n_bnrM, 0px))}._180k1wjm{inset-block-start:calc(var(--n_bnrM, 0px))}._26vxoned{padding-block-start:calc(var(--n_tNvM, 0px))}._1mt19dtb{margin-block-start:calc(var(--n_tNvM, 0px)*-1)}._p0az1elq{transform:translateX(calc(var(--n_snvRsz, var(--n_snvW, 0px)) - var(--ds-border-width, 1px)))}._dm2518uv{display:initial}._dm25glyw{display:none}._qiln1gzg{grid-area:main}._p5clglyw{border-inline-end:none}._4ap31bhr{background-color:var(--ds-surface-overlay,#fff)}._scbp130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}._qilnk0mc{grid-area:side-nav}._p5clia51{border-inline-end:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}._4ap3vuon{background-color:var(--ds-surface,#fff)}._scbpglyw{box-shadow:none}}
25
26
  @media (prefers-reduced-motion:no-preference) and (min-width:64rem){._17ly8iot{transition-property:transform,display}._177m1a5r{transition-behavior:allow-discrete}._1sg81gzg{grid-area:main}._vgub1mm8{transition-timing-function:cubic-bezier(0,.4,0,1)}._hh1u1p9u{transform:translateX(calc(-100%*var(--animation-direction)))}@starting-style{._s2eg1p9u{transform:translateX(calc(-100%*var(--animation-direction)))}}._1sg8k0mc{grid-area:side-nav}._vgub1ku9{transition-timing-function:cubic-bezier(.6,0,0,1)}._hjoifnf5{transition-duration:.2s}}
26
27
  @media (prefers-reduced-motion:no-preference) and (not (min-width:64rem)){._aadi1p9u{transform:translateX(calc(-100%*var(--animation-direction)))}}
27
28
  @supports not (-moz-appearance:none){@media (prefers-reduced-motion:no-preference){._139f8iot{transition-property:transform,display}._1tpvfnf5{transition-duration:.2s}._sylc1a5r{transition-behavior:allow-discrete}._1uwsjq3t{transform:translateX(-100%)}}@media (prefers-reduced-motion:no-preference){@starting-style{._oyeijq3t{transform:translateX(-100%)}}._139f8iot{transition-property:transform,display}._1tpvfnf5{transition-duration:.2s}._sylc1a5r{transition-behavior:allow-discrete}}}
@@ -48,6 +48,9 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
48
48
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
49
49
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
50
50
  var panelSplitterResizingVar = '--n_snvRsz';
51
+ // Used to share the side nav width with the panel splitter, which is rendered outside the side nav element
52
+ // but positioned to stay at its right edge.
53
+ var sideNavClampedWidthVar = '--n_snvW';
51
54
  var widthResizeBounds = {
52
55
  min: '240px',
53
56
  max: '50vw'
@@ -63,7 +66,7 @@ function getResizeBounds() {
63
66
  */
64
67
  var isFirefox = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
65
68
  var panelSplitterPortalTargetStyles = {
66
- root: "_kqswstnw _rjxpidpf _u7coidpf _4t3i1osq _165t56xv"
69
+ root: "_kqsw1n9t _1pbyegat _u7coidpf _t9ectl5p _4t3ieqxy _165t56xv _p0az1elq"
67
70
  };
68
71
  var styles = {
69
72
  root: "_nd5l1b6c _191wglyw _t51zglyw _bfhk1bhr _16qs130s _vchhusvi _4t3ieqxy _152timx3 _kqsw1if8 _1bsb1ego _1pbycs5v _14b5hc79 _qilnk0mc _p5clia51 _4ap3vuon _scbpglyw",
@@ -222,7 +225,7 @@ function SideNavInternal(_ref) {
222
225
  function open() {
223
226
  // Prevent the flyout from being opened if there are any open layers in the top nav start
224
227
  if (openLayerObserver && openLayerObserver.getCount({
225
- namespace: 'top-nav-start',
228
+ namespace: _constants.openLayerObserverTopNavStartNamespace,
226
229
  type: 'popup'
227
230
  }) > 0 && (0, _platformFeatureFlags.fg)('platform_dst_nav4_side_nav_resize_tooltip_feedback')) {
228
231
  return;
@@ -750,7 +753,7 @@ function SideNavInternal(_ref) {
750
753
  * Avoiding nesting the `@supports` at-rule inside of `@media` means Compiled can remove duplicate styles from the generated CSS.
751
754
  */
752
755
  !isFirefox;
753
- return /*#__PURE__*/_react.default.createElement("nav", (0, _extends2.default)({
756
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("nav", (0, _extends2.default)({
754
757
  id: id
755
758
  }, devTimeOnlyAttributes, {
756
759
  "data-layout-slot": true,
@@ -791,9 +794,11 @@ function SideNavInternal(_ref) {
791
794
  shortcut: isShortcutEnabled ? _sideNavToggleTooltipKeyboardShortcut.sideNavToggleTooltipKeyboardShortcut : undefined
792
795
  }, /*#__PURE__*/_react.default.createElement("div", {
793
796
  className: (0, _runtime.ax)([styles.flexContainer])
794
- }, children)), isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && /*#__PURE__*/_react.default.createElement("div", {
797
+ }, children))), isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && /*#__PURE__*/_react.default.createElement("div", {
795
798
  ref: panelSplitterPortalTargetRef,
796
- className: (0, _runtime.ax)([panelSplitterPortalTargetStyles.root])
799
+ "data-layout-slot": true,
800
+ style: (0, _defineProperty2.default)({}, sideNavClampedWidthVar, clampedWidth),
801
+ className: (0, _runtime.ax)([panelSplitterPortalTargetStyles.root, isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileOnly, !isExpandedOnDesktop && isExpandedOnMobile && !isFlyoutVisible && styles.hiddenDesktopOnly, !isExpandedOnDesktop && !isExpandedOnMobile && !isFlyoutVisible && styles.hiddenMobileAndDesktop])
797
802
  }));
798
803
  }
799
804
 
@@ -805,21 +810,21 @@ function SideNavInternal(_ref) {
805
810
  *
806
811
  * You can optionally render a `PanelSplitter` as a child to make the side navigation slot resizable.
807
812
  */
808
- function SideNav(_ref8) {
809
- var children = _ref8.children,
810
- defaultCollapsed = _ref8.defaultCollapsed,
811
- _ref8$defaultWidth = _ref8.defaultWidth,
812
- defaultWidth = _ref8$defaultWidth === void 0 ? 320 : _ref8$defaultWidth,
813
- testId = _ref8.testId,
814
- label = _ref8.label,
815
- _ref8$skipLinkLabel = _ref8.skipLinkLabel,
816
- skipLinkLabel = _ref8$skipLinkLabel === void 0 ? label : _ref8$skipLinkLabel,
817
- onExpand = _ref8.onExpand,
818
- onCollapse = _ref8.onCollapse,
819
- onPeekStart = _ref8.onPeekStart,
820
- onPeekEnd = _ref8.onPeekEnd,
821
- canToggleWithShortcut = _ref8.canToggleWithShortcut,
822
- id = _ref8.id;
813
+ function SideNav(_ref9) {
814
+ var children = _ref9.children,
815
+ defaultCollapsed = _ref9.defaultCollapsed,
816
+ _ref9$defaultWidth = _ref9.defaultWidth,
817
+ defaultWidth = _ref9$defaultWidth === void 0 ? 320 : _ref9$defaultWidth,
818
+ testId = _ref9.testId,
819
+ label = _ref9.label,
820
+ _ref9$skipLinkLabel = _ref9.skipLinkLabel,
821
+ skipLinkLabel = _ref9$skipLinkLabel === void 0 ? label : _ref9$skipLinkLabel,
822
+ onExpand = _ref9.onExpand,
823
+ onCollapse = _ref9.onCollapse,
824
+ onPeekStart = _ref9.onPeekStart,
825
+ onPeekEnd = _ref9.onPeekEnd,
826
+ canToggleWithShortcut = _ref9.canToggleWithShortcut,
827
+ id = _ref9.id;
823
828
  return /*#__PURE__*/_react.default.createElement(_openLayerObserver.OpenLayerObserverNamespaceProvider, {
824
829
  namespace: _constants.openLayerObserverSideNavNamespace
825
830
  }, /*#__PURE__*/_react.default.createElement(SideNavInternal, {
@@ -13,7 +13,6 @@
13
13
  ._1e0c11p5{display:grid}
14
14
  ._1gufidpf:after{inset-block-end:0}
15
15
  ._1pby11wp{z-index:3}
16
- ._1pbycs5v{z-index:2}
17
16
  ._1pbyegat{z-index:4}
18
17
  ._4cvr1h6o{align-items:center}
19
18
  ._4t3i1dgc{height:var(--n_tNvM)}
@@ -22,7 +22,6 @@ var _hoistSlotSizesContext = require("../hoist-slot-sizes-context");
22
22
  var _hoistUtils = require("../hoist-utils");
23
23
  var _idUtils = require("../id-utils");
24
24
  var _useSideNavVisibility2 = require("../side-nav/use-side-nav-visibility");
25
- var _useOpenLayerCount = require("../use-open-layer-count");
26
25
  var _excluded = ["backgroundColor"];
27
26
  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); }
28
27
  /**
@@ -37,8 +36,7 @@ var styles = {
37
36
  root: "_nd5l8cbt _zulpu2gc _18zrutpp _179ria51 _1e0c11p5 _yv0e1mfv _4cvr1h6o _bfhkvuon _vchhusvi _4t3i1dgc _152t1nws _kqsw1if8 _1pbyegat _d6vu1bgi _1j8b18ax",
38
37
  fullHeightSidebar: "_18zrze3t _179rglyw _bfhkglyw _lcxvglyw _pdlmutpp",
39
38
  fullHeightSidebarExpanded: "_1rqt16a9 _jh1g18ax",
40
- fullHeightSidebarWithLayeringFixes: "_1pbycs5v _lcxv1wug _bfhkvuon _aetrb3bt _18postnw _1gufidpf _1czdidpf _g0nf3tht _1beue4h9 _uaeunqa1 _1cte1l7x",
41
- fullHeightSidebarWithLayeringFixesAndOpenLayer: "_1pby11wp"
39
+ fullHeightSidebarWithLayeringFixes: "_1pby11wp _lcxv1wug _bfhkvuon _aetrb3bt _18postnw _1gufidpf _1czdidpf _g0nf3tht _1beue4h9 _uaeunqa1 _1cte1l7x"
42
40
  };
43
41
 
44
42
  /**
@@ -51,14 +49,6 @@ var backgroundStyles = {
51
49
  sideNavExpanded: "_hyzqcs5v"
52
50
  };
53
51
 
54
- /**
55
- * Namespaces to check for open layers within the top nav.
56
- * When there is an open layer in the top nav, the top nav is given a higher z-index than the side nav.
57
- *
58
- * Placed outside the component for stability, as the list is used as an effect dependency.
59
- */
60
- var topNavOpenLayerNamespaces = [_constants.openLayerObserverTopNavStartNamespace, _constants.openLayerObserverTopNavMiddleNamespace, _constants.openLayerObserverTopNavEndNamespace];
61
-
62
52
  /**
63
53
  * The top nav layout area. It will display at the top of the screen, below the banner if one is present.
64
54
  */
@@ -110,10 +100,6 @@ function TopNav(_ref) {
110
100
  foregroundStyle = _useMemo.foregroundStyle;
111
101
  var _useSideNavVisibility = (0, _useSideNavVisibility2.useSideNavVisibility)(),
112
102
  isExpandedOnDesktop = _useSideNavVisibility.isExpandedOnDesktop;
113
- var hasOpenPopup = (0, _useOpenLayerCount.useHasOpenLayers)({
114
- namespaces: topNavOpenLayerNamespaces,
115
- type: 'popup'
116
- });
117
103
  return /*#__PURE__*/React.createElement(_hasCustomThemeContext.HasCustomThemeContext.Provider, {
118
104
  value: customTheme.isEnabled
119
105
  }, isFhsEnabled && !(0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && /*#__PURE__*/React.createElement("div", {
@@ -126,7 +112,7 @@ function TopNav(_ref) {
126
112
  }), /*#__PURE__*/React.createElement("header", {
127
113
  id: id,
128
114
  "data-layout-slot": true,
129
- className: (0, _runtime.ax)([styles.root, isFhsEnabled && styles.fullHeightSidebar, isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && styles.fullHeightSidebarWithLayeringFixes, hasOpenPopup && isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && styles.fullHeightSidebarWithLayeringFixesAndOpenLayer, isExpandedOnDesktop && isFhsEnabled && styles.fullHeightSidebarExpanded, xcss]),
115
+ className: (0, _runtime.ax)([styles.root, isFhsEnabled && styles.fullHeightSidebar, isFhsEnabled && (0, _platformFeatureFlags.fg)('platform-dst-side-nav-layering-fixes') && styles.fullHeightSidebarWithLayeringFixes, isExpandedOnDesktop && isFhsEnabled && styles.fullHeightSidebarExpanded, xcss]),
130
116
  "data-testid": testId
131
117
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop
132
118
  ,
@@ -25,7 +25,6 @@ var styles = {
25
25
  elemAfter: "_yyhy11wp _1ii7kb7n _1e0c1txw",
26
26
  fullHeightSidebar: "_p12fktbq"
27
27
  };
28
-
29
28
  /**
30
29
  * __Search__
31
30
  *
@@ -1,7 +1,7 @@
1
1
 
2
2
  ._nd5ldkfm{grid-area:banner}._152tidpf{inset-block-start:0}
3
3
  ._18m915vq{overflow-y:hidden}
4
- ._1pbycs5v{z-index:2}
4
+ ._1pby11wp{z-index:3}
5
5
  ._1pbyegat{z-index:4}
6
6
  ._1reo15vq{overflow-x:hidden}
7
7
  ._4t3iutvi{height:var(--n_bnrM)}
@@ -12,7 +12,7 @@ import { DangerouslyHoistCssVarToDocumentRoot, HoistCssVarToLocalGrid } from './
12
12
  import { useLayoutId } from './id-utils';
13
13
  const styles = {
14
14
  root: "_nd5ldkfm _1reo15vq _18m915vq _4t3iutvi _152tidpf _kqsw1if8 _1pbyegat",
15
- fullHeightSidebarWithLayeringFixes: "_1pbycs5v"
15
+ fullHeightSidebarWithLayeringFixes: "_1pby11wp"
16
16
  };
17
17
 
18
18
  /**
@@ -48,14 +48,15 @@ export const UNSAFE_MAIN_INLINE_END_FOR_LEGACY_PAGES_ONLY = `calc(var(${UNSAFE_a
48
48
  * rely on accessing them through global means.
49
49
  */
50
50
  export const localSlotLayers = {
51
+ // The side nav panel splitter is layered above the top nav when FHS and 'platform-dst-side-nav-layering-fixes' is enabled.
52
+ // It has the same z-index value, but is rendered after the top nav in the DOM so is stacked above.
53
+ sideNavPanelSplitterFHS: 4,
51
54
  topBar: 4,
52
55
  banner: 4,
53
- // With the FHS layering refactors, the banner and top nav have a lower z-index to allow layers from the side nav to overlay them.
54
- // When they all have equal z-index values, the DOM order determines the layering - meaning the side nav will be layered above the rest.
55
- // But, when the top bar contains an open layer, it needs to be layered above the side nav, so has a higher value.
56
- topNavFHSWithOpenLayer: 3,
57
- bannerFHS: 2,
58
- topNavFHS: 2,
56
+ // When FHS and 'platform-dst-side-nav-layering-fixes' is enabled, the side nav is layered below the top nav,
57
+ // but above the panel
58
+ bannerFHS: 3,
59
+ topNavFHS: 3,
59
60
  sideNav: 2,
60
61
  panelSmallViewports: 1
61
62
  };
@@ -2,6 +2,7 @@
2
2
  ._12y31o36{outline-width:medium}
3
3
  ._152tidpf{inset-block-start:0}
4
4
  ._1bsb1l7b{width:3px}
5
+ ._1bsb1ris{width:max-content}
5
6
  ._1bsbl52n{width:17px}
6
7
  ._1e021v6z{inset-inline-start:7px}
7
8
  ._1e02zeo2{inset-inline-start:-9px}
@@ -42,7 +42,8 @@ const lineStyles = {
42
42
  root: "_kqswstnw _1e0c1ule _1bsb1l7b _4t3i1osq _syaz1kw7 _bfhk1r31 _1e021v6z"
43
43
  };
44
44
  const tooltipStyles = {
45
- root: "_ahbq196n"
45
+ root: "_ahbq196n",
46
+ fullHeightSidebarWithLayeringFixes: "_1bsb1ris"
46
47
  };
47
48
  const panelSplitterDragDataSymbol = Symbol('panel-splitter-drag-data');
48
49
  function signPanelSplitterDragData(data) {
@@ -60,7 +61,11 @@ const PanelSplitterTooltip = /*#__PURE__*/forwardRef(({
60
61
  if (!props.style || !props.style.transform) {
61
62
  return props.style;
62
63
  }
63
- const [translateX, translateY] = props.style.transform.matchAll(/\d+px/g);
64
+
65
+ // We cannot just match against integers safely, as browsers may introduce non-integer values due to scaling
66
+ // In the future we can probably use `CSSStyleValue.parse()` to get the browser to handle parsing for us,
67
+ // but there's no Firefox support yet.
68
+ const [translateX, translateY] = props.style.transform.matchAll(/(?:-|\+)?\d*\.?\d+px/g);
64
69
  if (!translateY) {
65
70
  // If we can't extract the translateY value we bail out and return the original style
66
71
  return props.style;
@@ -82,7 +87,7 @@ const PanelSplitterTooltip = /*#__PURE__*/forwardRef(({
82
87
  // Must be statically passed
83
88
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/design-system/no-unsafe-style-overrides
84
89
  ,
85
- className: ax([tooltipStyles.root, className])
90
+ className: ax([tooltipStyles.root, fg('platform-dst-side-nav-layering-fixes') && tooltipStyles.fullHeightSidebarWithLayeringFixes, className])
86
91
  // eslint-disable-next-line @atlaskit/design-system/no-unsafe-style-overrides
87
92
  ,
88
93
 
@@ -97,11 +102,13 @@ const PanelSplitterTooltip = /*#__PURE__*/forwardRef(({
97
102
  const MaybeTooltip = ({
98
103
  tooltipContent,
99
104
  shortcut,
100
- children
105
+ children,
106
+ testId
101
107
  }) => {
102
108
  const isFhsEnabled = useIsFhsEnabled();
103
109
  if (tooltipContent && isFhsEnabled) {
104
110
  return /*#__PURE__*/React.createElement(Tooltip, {
111
+ testId: testId,
105
112
  content: tooltipContent,
106
113
  shortcut: shortcut,
107
114
  position: fg('platform_dst_nav4_side_nav_resize_tooltip_feedback') ? 'mouse-y' : 'mouse',
@@ -375,7 +382,8 @@ const PortaledPanelSplitter = ({
375
382
  className: ax([containerStyles.root, position === 'start' && containerStyles.positionStart, position === 'end' && containerStyles.positionEnd])
376
383
  }, /*#__PURE__*/React.createElement(MaybeTooltip, {
377
384
  tooltipContent: tooltipContent,
378
- shortcut: shortcut
385
+ shortcut: shortcut,
386
+ testId: testId && fg('platform_dst_nav4_side_nav_resize_tooltip_feedback') ? `${testId}-tooltip` : undefined
379
387
  }, /*#__PURE__*/React.createElement("div", {
380
388
  ref: splitterRef,
381
389
  "data-testid": testId,
@@ -1,10 +1,10 @@
1
- import React, { useContext } from 'react';
1
+ import React, { useContext, useEffect, useState } from 'react';
2
2
  import invariant from 'tiny-invariant';
3
+ import { useOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
3
4
  import { fg } from '@atlaskit/platform-feature-flags';
4
5
  import { useIsFhsEnabled } from '../../fhs-rollout/use-is-fhs-enabled';
5
6
  import { openLayerObserverSideNavNamespace, openLayerObserverTopNavEndNamespace, openLayerObserverTopNavMiddleNamespace, openLayerObserverTopNavStartNamespace, sideNavPanelSplitterId } from '../constants';
6
7
  import { useToggleSideNav } from '../side-nav/use-toggle-side-nav';
7
- import { useHasOpenLayers } from '../use-open-layer-count';
8
8
  import { OnDoubleClickContext, PanelSplitterContext } from './context';
9
9
  import { PanelSplitter } from './panel-splitter';
10
10
 
@@ -14,14 +14,47 @@ import { PanelSplitter } from './panel-splitter';
14
14
  * Placed outside the component for stability, as the list is used as an effect dependency.
15
15
  */
16
16
  const openLayerNamespacesToCheck = [
17
- // We don't technically need to check the side nav for open layers, as they wouldn't overlay the
18
- // panel splitter, as it sits within the same stacking context as the side nav. For consistency however,
19
- // we check it as well.
17
+ // The side nav panel splitter is layered above the side nav, so needs to be disabled when there are open popups in the side nav.
20
18
  openLayerObserverSideNavNamespace,
21
- // When there is an open layer in the top nav, the top nav is given a higher z-index than the side nav.
22
- // This means the part of the side nav panel splitter that was sitting above the top nav will no longer
23
- // be interactive (as it is now behind the top nav). So, we need to disable the entire panel splitter.
19
+ // The side nav panel splitter is layered above the top nav, so needs to be disabled when there are open popups in the top nav.
24
20
  openLayerObserverTopNavStartNamespace, openLayerObserverTopNavMiddleNamespace, openLayerObserverTopNavEndNamespace];
21
+
22
+ /**
23
+ * Returns whether there are any open popups in the side nav or top nav
24
+ */
25
+ function useHasOpenPopupsInSideNavOrTopNav() {
26
+ const isFhsEnabled = useIsFhsEnabled();
27
+ const openLayerObserver = useOpenLayerObserver();
28
+ // Setting the initial state to false, as it is unlikely that there would be any open popups when the app starts.
29
+ const [hasOpenPopups, setHasOpenPopups] = useState(false);
30
+ useEffect(() => {
31
+ if (!openLayerObserver || !isFhsEnabled || !fg('platform-dst-side-nav-layering-fixes')) {
32
+ return;
33
+ }
34
+ function updateState() {
35
+ if (!openLayerObserver) {
36
+ return;
37
+ }
38
+ const hasAnyOpenLayers = openLayerNamespacesToCheck.some(namespace => openLayerObserver.getCount({
39
+ namespace,
40
+ type: 'popup'
41
+ }) > 0);
42
+ setHasOpenPopups(hasAnyOpenLayers);
43
+ }
44
+
45
+ // Initial check. We do this _in case_ a popup is already open when the component mounts.
46
+ updateState();
47
+
48
+ // Subscribe to each namespace
49
+ const cleanups = openLayerNamespacesToCheck.map(namespace => openLayerObserver.onChange(updateState, {
50
+ namespace
51
+ }));
52
+ return function cleanupHook() {
53
+ cleanups.forEach(cleanup => cleanup());
54
+ };
55
+ }, [isFhsEnabled, openLayerObserver]);
56
+ return hasOpenPopups;
57
+ }
25
58
  /**
26
59
  * _SideNavPanelSplitter_
27
60
  *
@@ -53,11 +86,8 @@ export const SideNavPanelSplitter = ({
53
86
  // The logic and state for disabling the panel splitter when there are open popups
54
87
  // in the side nav or top nav is being placed here, instead of in `SideNav`, to prevent
55
88
  // re-rendering the side nav anytime the number of open popups changes.
56
- const hasOpenLayers = useHasOpenLayers({
57
- namespaces: openLayerNamespacesToCheck,
58
- type: 'popup'
59
- });
60
- if (hasOpenLayers && isFhsEnabled && fg('platform-dst-side-nav-layering-fixes')) {
89
+ const hasOpenLayersInSideNavOrTopNav = useHasOpenPopupsInSideNavOrTopNav();
90
+ if (hasOpenLayersInSideNavOrTopNav && isFhsEnabled && fg('platform-dst-side-nav-layering-fixes')) {
61
91
  return null;
62
92
  }
63
93
  return /*#__PURE__*/React.createElement(OnDoubleClickContext.Provider, {