@atlaskit/navigation-system 11.0.0 → 11.1.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 +12 -0
- package/dist/cjs/entry-points/layout/chat-panel.js +12 -0
- package/dist/cjs/entry-points/layout/layout-sizing.js +44 -0
- package/dist/cjs/ui/page-layout/calculate-layout-area-states.js +179 -0
- package/dist/cjs/ui/page-layout/chat-panel.compiled.css +27 -0
- package/dist/cjs/ui/page-layout/chat-panel.js +216 -0
- package/dist/cjs/ui/page-layout/constants.js +6 -1
- package/dist/cjs/ui/page-layout/layout-area-sizing-context.js +13 -0
- package/dist/cjs/ui/page-layout/layout-area-sizing-provider.js +304 -0
- package/dist/cjs/ui/page-layout/layout-area-state-contexts.js +13 -0
- package/dist/cjs/ui/page-layout/main/main.js +11 -2
- package/dist/cjs/ui/page-layout/panel-splitter/panel-splitter.js +73 -7
- package/dist/cjs/ui/page-layout/panel-splitter/provider.js +7 -1
- package/dist/cjs/ui/page-layout/resolve-layout-width.js +13 -0
- package/dist/cjs/ui/page-layout/ribbon.js +1 -0
- package/dist/cjs/ui/page-layout/root.compiled.css +4 -2
- package/dist/cjs/ui/page-layout/root.js +10 -4
- package/dist/cjs/ui/page-layout/side-nav/side-nav.compiled.css +2 -1
- package/dist/cjs/ui/page-layout/side-nav/side-nav.js +152 -14
- package/dist/cjs/ui/page-layout/use-layout-area-sizing.js +73 -0
- package/dist/cjs/ui/page-layout/use-layout-main-sizing.js +15 -0
- package/dist/cjs/ui/page-layout/use-occupied-layout-width.js +111 -0
- package/dist/es2019/entry-points/layout/chat-panel.js +1 -0
- package/dist/es2019/entry-points/layout/layout-sizing.js +3 -0
- package/dist/es2019/ui/page-layout/calculate-layout-area-states.js +128 -0
- package/dist/es2019/ui/page-layout/chat-panel.compiled.css +27 -0
- package/dist/es2019/ui/page-layout/chat-panel.js +199 -0
- package/dist/es2019/ui/page-layout/constants.js +5 -0
- package/dist/es2019/ui/page-layout/layout-area-sizing-context.js +8 -0
- package/dist/es2019/ui/page-layout/layout-area-sizing-provider.js +279 -0
- package/dist/es2019/ui/page-layout/layout-area-state-contexts.js +7 -0
- package/dist/es2019/ui/page-layout/main/main.js +10 -2
- package/dist/es2019/ui/page-layout/panel-splitter/panel-splitter.js +68 -8
- package/dist/es2019/ui/page-layout/panel-splitter/provider.js +7 -1
- package/dist/es2019/ui/page-layout/resolve-layout-width.js +7 -0
- package/dist/es2019/ui/page-layout/ribbon.js +1 -0
- package/dist/es2019/ui/page-layout/root.compiled.css +4 -2
- package/dist/es2019/ui/page-layout/root.js +10 -4
- package/dist/es2019/ui/page-layout/side-nav/side-nav.compiled.css +2 -1
- package/dist/es2019/ui/page-layout/side-nav/side-nav.js +142 -14
- package/dist/es2019/ui/page-layout/use-layout-area-sizing.js +58 -0
- package/dist/es2019/ui/page-layout/use-layout-main-sizing.js +7 -0
- package/dist/es2019/ui/page-layout/use-occupied-layout-width.js +67 -0
- package/dist/esm/entry-points/layout/chat-panel.js +1 -0
- package/dist/esm/entry-points/layout/layout-sizing.js +3 -0
- package/dist/esm/ui/page-layout/calculate-layout-area-states.js +173 -0
- package/dist/esm/ui/page-layout/chat-panel.compiled.css +27 -0
- package/dist/esm/ui/page-layout/chat-panel.js +207 -0
- package/dist/esm/ui/page-layout/constants.js +5 -0
- package/dist/esm/ui/page-layout/layout-area-sizing-context.js +8 -0
- package/dist/esm/ui/page-layout/layout-area-sizing-provider.js +295 -0
- package/dist/esm/ui/page-layout/layout-area-state-contexts.js +7 -0
- package/dist/esm/ui/page-layout/main/main.js +11 -2
- package/dist/esm/ui/page-layout/panel-splitter/panel-splitter.js +74 -8
- package/dist/esm/ui/page-layout/panel-splitter/provider.js +7 -1
- package/dist/esm/ui/page-layout/resolve-layout-width.js +7 -0
- package/dist/esm/ui/page-layout/ribbon.js +1 -0
- package/dist/esm/ui/page-layout/root.compiled.css +4 -2
- package/dist/esm/ui/page-layout/root.js +10 -4
- package/dist/esm/ui/page-layout/side-nav/side-nav.compiled.css +2 -1
- package/dist/esm/ui/page-layout/side-nav/side-nav.js +153 -15
- package/dist/esm/ui/page-layout/use-layout-area-sizing.js +67 -0
- package/dist/esm/ui/page-layout/use-layout-main-sizing.js +9 -0
- package/dist/esm/ui/page-layout/use-occupied-layout-width.js +105 -0
- package/dist/types/entry-points/layout/chat-panel.d.ts +1 -0
- package/dist/types/entry-points/layout/layout-sizing.d.ts +4 -0
- package/dist/types/ui/page-layout/calculate-layout-area-states.d.ts +20 -0
- package/dist/types/ui/page-layout/chat-panel.d.ts +48 -0
- package/dist/types/ui/page-layout/constants.d.ts +4 -0
- package/dist/types/ui/page-layout/layout-area-sizing-context.d.ts +55 -0
- package/dist/types/ui/page-layout/layout-area-sizing-provider.d.ts +5 -0
- package/dist/types/ui/page-layout/layout-area-state-contexts.d.ts +3 -0
- package/dist/types/ui/page-layout/main/main.d.ts +5 -1
- package/dist/types/ui/page-layout/panel-splitter/panel-splitter-context.d.ts +15 -1
- package/dist/types/ui/page-layout/panel-splitter/provider.d.ts +1 -1
- package/dist/types/ui/page-layout/resolve-layout-width.d.ts +2 -0
- package/dist/types/ui/page-layout/side-nav/side-nav.d.ts +11 -2
- package/dist/types/ui/page-layout/use-layout-area-sizing.d.ts +12 -0
- package/dist/types/ui/page-layout/use-layout-main-sizing.d.ts +2 -0
- package/dist/types/ui/page-layout/use-occupied-layout-width.d.ts +8 -0
- package/layout/chat-panel/package.json +10 -0
- package/layout/layout-sizing/package.json +10 -0
- package/package.json +11 -3
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
._12xt1ym0._12xt1ym0{grid-area:chat-panel}
|
|
2
|
+
._12xtquub._12xtquub{grid-area:2/1/-1/-1}
|
|
3
|
+
._nd5lquub{grid-area:2/1/-1/-1}
|
|
4
|
+
._191wglyw{border-inline-start:none}
|
|
5
|
+
._i8luglyw._i8luglyw{border-inline-start:none}
|
|
6
|
+
._i8luia51._i8luia51{border-inline-start:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}._10fph9n0{justify-self:end}
|
|
7
|
+
._152t1nws{inset-block-start:var(--n_bnrM,0)}
|
|
8
|
+
._16hg1bhr._16hg1bhr{background-color:var(--ds-surface-overlay,#fff)}
|
|
9
|
+
._16hgvuon._16hgvuon{background-color:var(--ds-surface,#fff)}
|
|
10
|
+
._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
|
|
11
|
+
._18m91wug{overflow-y:auto}
|
|
12
|
+
._1bsb1ky5{width:var(--n_cPnlRsz,var(--n_cPnlOverlayW))}
|
|
13
|
+
._1pbyqfx8{z-index:5}
|
|
14
|
+
._1reo1wug{overflow-x:auto}
|
|
15
|
+
._4t3i1osq{height:100%}
|
|
16
|
+
._4t3i56xv{height:calc(100vh - var(--n_bnrM, 0px))}
|
|
17
|
+
._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
|
|
18
|
+
._kqsw1if8{position:sticky}
|
|
19
|
+
._p12f1osq{max-width:100%}
|
|
20
|
+
._qo371wug._qo371wug{z-index:auto}
|
|
21
|
+
._qo37qfx8._qo37qfx8{z-index:5}
|
|
22
|
+
._so1e130s._so1e130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
|
|
23
|
+
._so1eglyw._so1eglyw{box-shadow:none}
|
|
24
|
+
._vchhusvi{box-sizing:border-box}
|
|
25
|
+
._w1ap1ky5._w1ap1ky5{width:var(--n_cPnlRsz,var(--n_cPnlOverlayW))}
|
|
26
|
+
._w1ap8rg1._w1ap8rg1{width:var(--n_cPnlRsz,var(--n_cPnlW))}
|
|
27
|
+
@media (min-width:40rem){._19dmia51{border-inline-start:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}._1ao7glyw{box-shadow:none}._qhdr1ym0{grid-area:chat-panel}._m5h51wug{z-index:auto}._1ttbvuon{background-color:var(--ds-surface,#fff)}._1mxh8rg1{width:var(--n_cPnlRsz,var(--n_cPnlW))}}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/* chat-panel.tsx generated by @compiled/babel-plugin v3.0.2 */
|
|
2
|
+
import "./chat-panel.compiled.css";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
6
|
+
import { bind } from 'bind-event-listener';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
8
|
+
import { useSkipLinkInternal } from '../../context/skip-links/use-skip-link-internal';
|
|
9
|
+
import { chatPanelLiveWidthVar, chatPanelVar, localSlotLayers, mainMinimumWidthVar } from './constants';
|
|
10
|
+
import { convertResizeBoundToPixels } from './panel-splitter/convert-resize-bound-to-pixels';
|
|
11
|
+
import { PanelSplitterProvider } from './panel-splitter/provider';
|
|
12
|
+
import { resolveLayoutWidth } from './resolve-layout-width';
|
|
13
|
+
import { gridRootId } from './root';
|
|
14
|
+
import { useLayoutAreaSizing } from './use-layout-area-sizing';
|
|
15
|
+
import { useLayoutId } from './use-layout-id';
|
|
16
|
+
import { useResizingWidthCssVarOnRootElement } from './use-resizing-width-css-var-on-root-element';
|
|
17
|
+
import { useSafeDefaultWidth } from './use-safe-default-width';
|
|
18
|
+
const chatPanelPanelSplitterId = Symbol('ChatPanel PanelSplitter');
|
|
19
|
+
const panelSplitterResizingVar = '--n_cPnlRsz';
|
|
20
|
+
const chatPanelOverlayWidthVar = '--n_cPnlOverlayW';
|
|
21
|
+
const fallbackDefaultWidth = 400;
|
|
22
|
+
const fallbackCompactDefaultWidth = 320;
|
|
23
|
+
const fallbackMinWidth = '320px';
|
|
24
|
+
const inlineMediaQuery = '(min-width: 40rem)';
|
|
25
|
+
function getPixelCustomProperty(element, propertyName, fallback) {
|
|
26
|
+
const raw = getComputedStyle(element).getPropertyValue(propertyName).trim();
|
|
27
|
+
const value = Number.parseFloat(raw);
|
|
28
|
+
return Number.isFinite(value) ? resolveLayoutWidth(raw.endsWith('vw') ? `${value}vw` : value, window.innerWidth) : fallback;
|
|
29
|
+
}
|
|
30
|
+
const styles = {
|
|
31
|
+
root: "_nd5lquub _10fph9n0 _vchhusvi _p12f1osq _1pbyqfx8 _4t3i56xv _kqsw1if8 _152t1nws _bfhk1bhr _1bsb1ky5 _qhdr1ym0 _m5h51wug _1ttbvuon _1mxh8rg1",
|
|
32
|
+
managedInline: "_12xt1ym0 _qo371wug _16hgvuon _w1ap8rg1",
|
|
33
|
+
managedOverlay: "_12xtquub _qo37qfx8 _16hg1bhr _w1ap1ky5",
|
|
34
|
+
scrollContainer: "_1reo1wug _18m91wug _4t3i1osq",
|
|
35
|
+
border: "_191wglyw _16qs130s _19dmia51 _1ao7glyw",
|
|
36
|
+
managedInlineBorder: "_i8luia51 _so1eglyw",
|
|
37
|
+
managedOverlayBorder: "_i8luglyw _so1e130s"
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The ChatPanel layout area is rendered below the banner and to the right (inline end) of the app.
|
|
42
|
+
*
|
|
43
|
+
* Below 40rem it overlays the app. At 40rem and above it is rendered as the final
|
|
44
|
+
* full-height column in the page layout grid.
|
|
45
|
+
*
|
|
46
|
+
* You can optionally render a `PanelSplitter` as a child to make the chat panel resizable.
|
|
47
|
+
*/
|
|
48
|
+
export function ChatPanel({
|
|
49
|
+
children,
|
|
50
|
+
defaultWidth: defaultWidthProp,
|
|
51
|
+
minWidth = fallbackMinWidth,
|
|
52
|
+
maxWidth,
|
|
53
|
+
label = 'Chat panel',
|
|
54
|
+
skipLinkLabel = label,
|
|
55
|
+
testId,
|
|
56
|
+
id: providedId,
|
|
57
|
+
xcss,
|
|
58
|
+
hasBorder = true,
|
|
59
|
+
onClose
|
|
60
|
+
}) {
|
|
61
|
+
var _managedSizing$mode, _managedSizing$width;
|
|
62
|
+
const id = useLayoutId({
|
|
63
|
+
providedId
|
|
64
|
+
});
|
|
65
|
+
const defaultWidth = useSafeDefaultWidth({
|
|
66
|
+
defaultWidthProp: defaultWidthProp !== null && defaultWidthProp !== void 0 ? defaultWidthProp : fallbackDefaultWidth,
|
|
67
|
+
fallbackDefaultWidth,
|
|
68
|
+
slotName: 'ChatPanel'
|
|
69
|
+
});
|
|
70
|
+
const compactDefaultWidth = defaultWidthProp === undefined ? fallbackCompactDefaultWidth : defaultWidth;
|
|
71
|
+
useSkipLinkInternal({
|
|
72
|
+
id,
|
|
73
|
+
label: skipLinkLabel,
|
|
74
|
+
isHidden: defaultWidth === 0 || fg('platform_dst_nav4_skip_link_a11y_1')
|
|
75
|
+
});
|
|
76
|
+
const ref = useRef(null);
|
|
77
|
+
const [inlineWidth, setInlineWidth] = useState(defaultWidth);
|
|
78
|
+
const [overlayWidth, setOverlayWidth] = useState(compactDefaultWidth);
|
|
79
|
+
const [isInline, setIsInline] = useState(false);
|
|
80
|
+
const {
|
|
81
|
+
state: managedSizing,
|
|
82
|
+
getResizeBounds: getManagedResizeBounds,
|
|
83
|
+
startResize,
|
|
84
|
+
resize,
|
|
85
|
+
completeResize
|
|
86
|
+
} = useLayoutAreaSizing({
|
|
87
|
+
area: 'chat-panel',
|
|
88
|
+
config: {
|
|
89
|
+
defaultWidth,
|
|
90
|
+
compactDefaultWidth,
|
|
91
|
+
minWidth,
|
|
92
|
+
maxWidth,
|
|
93
|
+
isOpen: true,
|
|
94
|
+
onRequestClose: onClose
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
const mediaQuery = window.matchMedia(inlineMediaQuery);
|
|
99
|
+
setIsInline(mediaQuery.matches);
|
|
100
|
+
const onChange = event => {
|
|
101
|
+
setIsInline(event.matches);
|
|
102
|
+
};
|
|
103
|
+
return bind(mediaQuery, {
|
|
104
|
+
type: 'change',
|
|
105
|
+
listener: onChange
|
|
106
|
+
});
|
|
107
|
+
}, []);
|
|
108
|
+
const mode = (_managedSizing$mode = managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) !== null && _managedSizing$mode !== void 0 ? _managedSizing$mode : isInline ? 'inline' : 'overlay';
|
|
109
|
+
const renderedWidth = (_managedSizing$width = managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.width) !== null && _managedSizing$width !== void 0 ? _managedSizing$width : mode === 'inline' ? inlineWidth : overlayWidth;
|
|
110
|
+
useResizingWidthCssVarOnRootElement({
|
|
111
|
+
isEnabled: !managedSizing,
|
|
112
|
+
cssVar: panelSplitterResizingVar,
|
|
113
|
+
panelId: chatPanelPanelSplitterId
|
|
114
|
+
});
|
|
115
|
+
const getResizeBounds = useCallback(() => {
|
|
116
|
+
const managedBounds = getManagedResizeBounds();
|
|
117
|
+
if (managedBounds) {
|
|
118
|
+
return managedBounds;
|
|
119
|
+
}
|
|
120
|
+
const chatPanel = ref.current;
|
|
121
|
+
const layoutRoot = chatPanel === null || chatPanel === void 0 ? void 0 : chatPanel.parentElement;
|
|
122
|
+
const main = layoutRoot === null || layoutRoot === void 0 ? void 0 : layoutRoot.querySelector(':scope > [data-layout-slot][role="main"]');
|
|
123
|
+
if (!chatPanel || !main || getComputedStyle(chatPanel).gridArea !== 'chat-panel') {
|
|
124
|
+
return {
|
|
125
|
+
min: minWidth,
|
|
126
|
+
max: maxWidth !== null && maxWidth !== void 0 ? maxWidth : '90vw'
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const chatPanelWidth = chatPanel.getBoundingClientRect().width;
|
|
130
|
+
const mainWidth = main.getBoundingClientRect().width;
|
|
131
|
+
if (chatPanelWidth === 0 || mainWidth === 0) {
|
|
132
|
+
return {
|
|
133
|
+
min: minWidth,
|
|
134
|
+
max: maxWidth !== null && maxWidth !== void 0 ? maxWidth : '90vw'
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const localPanel = main.querySelector('[data-layout-with-panel-slot]');
|
|
138
|
+
const isLocalPanelInline = localPanel && getComputedStyle(localPanel).gridArea === 'panel';
|
|
139
|
+
const localPanelWidth = isLocalPanelInline ? localPanel.getBoundingClientRect().width : 0;
|
|
140
|
+
const mainMinimumWidth = getPixelCustomProperty(main, mainMinimumWidthVar, 320);
|
|
141
|
+
const mainContentWidth = mainWidth - localPanelWidth;
|
|
142
|
+
const availableWidth = Math.max(0, mainContentWidth - mainMinimumWidth);
|
|
143
|
+
const availableInlineWidth = Math.floor(chatPanelWidth + availableWidth);
|
|
144
|
+
const constrainedMaxWidth = maxWidth ? Math.min(convertResizeBoundToPixels(maxWidth), availableInlineWidth) : availableInlineWidth;
|
|
145
|
+
return {
|
|
146
|
+
min: minWidth,
|
|
147
|
+
max: `${Math.max(convertResizeBoundToPixels(minWidth), constrainedMaxWidth)}px`
|
|
148
|
+
};
|
|
149
|
+
}, [getManagedResizeBounds, maxWidth, minWidth]);
|
|
150
|
+
const configuredMaxWidth = maxWidth !== null && maxWidth !== void 0 ? maxWidth : '100vw';
|
|
151
|
+
const chatPanelVariableWidth = managedSizing ? `${renderedWidth}px` : `clamp(${minWidth}, ${inlineWidth}px, ${configuredMaxWidth})`;
|
|
152
|
+
const chatPanelOverlayVariableWidth = managedSizing ? `${renderedWidth}px` : `clamp(${minWidth}, ${overlayWidth}px, min(90vw, ${configuredMaxWidth}))`;
|
|
153
|
+
const onCompleteResize = useCallback(finalWidth => {
|
|
154
|
+
if (mode === 'inline') {
|
|
155
|
+
setInlineWidth(finalWidth);
|
|
156
|
+
} else {
|
|
157
|
+
setOverlayWidth(finalWidth);
|
|
158
|
+
}
|
|
159
|
+
completeResize(mode, finalWidth);
|
|
160
|
+
}, [completeResize, mode]);
|
|
161
|
+
const onResizeStartInternal = useCallback(() => {
|
|
162
|
+
startResize(mode);
|
|
163
|
+
}, [mode, startResize]);
|
|
164
|
+
const onResizeInternal = useCallback(width => resize(mode, width), [mode, resize]);
|
|
165
|
+
return /*#__PURE__*/React.createElement("section", {
|
|
166
|
+
id: id,
|
|
167
|
+
"data-layout-slot": true,
|
|
168
|
+
"aria-label": label,
|
|
169
|
+
className: ax([styles.root, hasBorder && styles.border, (managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) === 'inline' && styles.managedInline, (managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) === 'overlay' && styles.managedOverlay, hasBorder && (managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) === 'inline' && styles.managedInlineBorder, hasBorder && (managedSizing === null || managedSizing === void 0 ? void 0 : managedSizing.mode) === 'overlay' && styles.managedOverlayBorder, xcss]),
|
|
170
|
+
style: {
|
|
171
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Shared layout CSS variable name.
|
|
172
|
+
[chatPanelVar]: chatPanelVariableWidth,
|
|
173
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/ui-styling-standard/no-unsafe-values
|
|
174
|
+
[chatPanelOverlayWidthVar]: chatPanelOverlayVariableWidth
|
|
175
|
+
},
|
|
176
|
+
"data-testid": testId,
|
|
177
|
+
"data-layout-chat-panel": "",
|
|
178
|
+
ref: ref
|
|
179
|
+
},
|
|
180
|
+
/*#__PURE__*/
|
|
181
|
+
// Hoist the committed or actively resizing inline width so SideNav can yield
|
|
182
|
+
// space to ChatPanel without allowing the page layout to overflow.
|
|
183
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
|
|
184
|
+
React.createElement("style", null, `
|
|
185
|
+
#${gridRootId} { ${chatPanelLiveWidthVar}: var(${panelSplitterResizingVar}, ${chatPanelVariableWidth}); }
|
|
186
|
+
`), /*#__PURE__*/React.createElement(PanelSplitterProvider, {
|
|
187
|
+
panelId: chatPanelPanelSplitterId,
|
|
188
|
+
panelRef: ref,
|
|
189
|
+
panelWidth: renderedWidth,
|
|
190
|
+
onCompleteResize: onCompleteResize,
|
|
191
|
+
onResizeStartInternal: managedSizing ? onResizeStartInternal : undefined,
|
|
192
|
+
onResizeInternal: managedSizing ? onResizeInternal : undefined,
|
|
193
|
+
getResizeBounds: getResizeBounds,
|
|
194
|
+
resizingCssVar: panelSplitterResizingVar,
|
|
195
|
+
position: "start"
|
|
196
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
197
|
+
className: ax([styles.scrollContainer])
|
|
198
|
+
}, children)));
|
|
199
|
+
}
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
export const sideNavVar = '--n_sNvw';
|
|
9
9
|
export const asideVar = '--n_asDw';
|
|
10
10
|
export const panelVar = '--n_pnlW';
|
|
11
|
+
export const chatPanelVar = '--n_cPnlW';
|
|
12
|
+
export const chatPanelLiveWidthVar = '--n_cPnlLw';
|
|
13
|
+
export const mainMinimumWidthVar = '--n_mainMinW';
|
|
11
14
|
export const bannerMountedVar = '--n_bnrM';
|
|
12
15
|
export const topNavMountedVar = '--n_tNvM';
|
|
13
16
|
export const ribbonVar = '--n_rbnW';
|
|
@@ -53,6 +56,8 @@ export const UNSAFE_MAIN_INLINE_END_FOR_LEGACY_PAGES_ONLY = `calc(var(${UNSAFE_a
|
|
|
53
56
|
*/
|
|
54
57
|
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
55
58
|
export const localSlotLayers = {
|
|
59
|
+
// Chat overlays the app, including TopNav and SideNav, below the banner.
|
|
60
|
+
chatPanelOverlay: 5,
|
|
56
61
|
ribbon: 4,
|
|
57
62
|
// The side nav panel splitter is layered above the top nav when FHS is enabled.
|
|
58
63
|
// It has the same z-index value, but is rendered after the top nav in the DOM so is stacked above.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Effective sibling widths held fixed during and after a user resize, until the
|
|
5
|
+
* layout dimensions or configuration change. This is not a resize history.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const LayoutAreaSizingContext = /*#__PURE__*/createContext(null);
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { bind } from 'bind-event-listener';
|
|
3
|
+
import { calculateLayoutAreaStates } from './calculate-layout-area-states';
|
|
4
|
+
import { LayoutAreaSizingContext } from './layout-area-sizing-context';
|
|
5
|
+
import { layoutAreaStateContexts } from './layout-area-state-contexts';
|
|
6
|
+
import { useOccupiedLayoutWidth } from './use-occupied-layout-width';
|
|
7
|
+
const defaultMainMinWidth = 320;
|
|
8
|
+
function useStableAreaState(state) {
|
|
9
|
+
const {
|
|
10
|
+
mode,
|
|
11
|
+
width,
|
|
12
|
+
minWidth
|
|
13
|
+
} = state !== null && state !== void 0 ? state : {};
|
|
14
|
+
return useMemo(() => mode !== undefined && width !== undefined && minWidth !== undefined ? {
|
|
15
|
+
mode,
|
|
16
|
+
width,
|
|
17
|
+
minWidth
|
|
18
|
+
} : undefined, [mode, width, minWidth]);
|
|
19
|
+
}
|
|
20
|
+
export function LayoutAreaSizingProvider({
|
|
21
|
+
children,
|
|
22
|
+
layoutRef
|
|
23
|
+
}) {
|
|
24
|
+
const occupiedWidth = useOccupiedLayoutWidth(layoutRef);
|
|
25
|
+
const [dimensions, setDimensions] = useState(null);
|
|
26
|
+
const [mainMinWidth, setMainMinWidth] = useState(defaultMainMinWidth);
|
|
27
|
+
const [areas, setAreas] = useState({});
|
|
28
|
+
const [resizeSession, setResizeSession] = useState(null);
|
|
29
|
+
const areaStatesRef = React.useRef({});
|
|
30
|
+
const configsRef = React.useRef({});
|
|
31
|
+
const openSequence = React.useRef(0);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setResizeSession(null);
|
|
34
|
+
}, [mainMinWidth, occupiedWidth]);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const root = layoutRef === null || layoutRef === void 0 ? void 0 : layoutRef.current;
|
|
37
|
+
let previous = null;
|
|
38
|
+
// Match the actual rem-based grid breakpoint, including browser font settings.
|
|
39
|
+
const inlineChatQuery = window.matchMedia('(min-width: 40rem)');
|
|
40
|
+
const inlineSideNavQuery = window.matchMedia('(min-width: 64rem)');
|
|
41
|
+
const updateDimensions = contentWidth => {
|
|
42
|
+
var _style$paddingLeft, _style$paddingRight, _previous;
|
|
43
|
+
const style = root && contentWidth === undefined ? getComputedStyle(root) : null;
|
|
44
|
+
const layoutWidth = contentWidth !== null && contentWidth !== void 0 ? contentWidth : root ? Math.max(0, root.clientWidth - (parseFloat((_style$paddingLeft = style === null || style === void 0 ? void 0 : style.paddingLeft) !== null && _style$paddingLeft !== void 0 ? _style$paddingLeft : '') || 0) - (parseFloat((_style$paddingRight = style === null || style === void 0 ? void 0 : style.paddingRight) !== null && _style$paddingRight !== void 0 ? _style$paddingRight : '') || 0)) : window.innerWidth;
|
|
45
|
+
const next = {
|
|
46
|
+
viewportWidth: window.innerWidth,
|
|
47
|
+
layoutWidth,
|
|
48
|
+
isChatPanelOverlay: !inlineChatQuery.matches,
|
|
49
|
+
isSideNavOverlay: !inlineSideNavQuery.matches
|
|
50
|
+
};
|
|
51
|
+
if (((_previous = previous) === null || _previous === void 0 ? void 0 : _previous.viewportWidth) === next.viewportWidth && previous.layoutWidth === next.layoutWidth && previous.isChatPanelOverlay === next.isChatPanelOverlay && previous.isSideNavOverlay === next.isSideNavOverlay) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
previous = next;
|
|
55
|
+
setDimensions(next);
|
|
56
|
+
setResizeSession(null);
|
|
57
|
+
};
|
|
58
|
+
updateDimensions();
|
|
59
|
+
const observer = new ResizeObserver(entries => {
|
|
60
|
+
const entry = entries.find(entry => entry.target === root);
|
|
61
|
+
if (entry) {
|
|
62
|
+
updateDimensions(entry.contentRect.width);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
if (root) {
|
|
66
|
+
observer.observe(root);
|
|
67
|
+
}
|
|
68
|
+
const unbindResize = bind(window, {
|
|
69
|
+
type: 'resize',
|
|
70
|
+
listener: () => updateDimensions()
|
|
71
|
+
});
|
|
72
|
+
const unbindQuery = bind(inlineChatQuery, {
|
|
73
|
+
type: 'change',
|
|
74
|
+
listener: () => updateDimensions()
|
|
75
|
+
});
|
|
76
|
+
const unbindSideNavQuery = bind(inlineSideNavQuery, {
|
|
77
|
+
type: 'change',
|
|
78
|
+
listener: () => updateDimensions()
|
|
79
|
+
});
|
|
80
|
+
return () => {
|
|
81
|
+
observer.disconnect();
|
|
82
|
+
unbindResize();
|
|
83
|
+
unbindQuery();
|
|
84
|
+
unbindSideNavQuery();
|
|
85
|
+
};
|
|
86
|
+
}, [layoutRef]);
|
|
87
|
+
const registerArea = useCallback((area, config) => {
|
|
88
|
+
const previous = configsRef.current[area];
|
|
89
|
+
configsRef.current[area] = config;
|
|
90
|
+
setResizeSession(current => {
|
|
91
|
+
// A changed layout/configuration needs a fresh allocation. A controlled
|
|
92
|
+
// acknowledgement for the resized area keeps its siblings fixed.
|
|
93
|
+
return (previous === null || previous === void 0 ? void 0 : previous.isOpen) === config.isOpen && previous.minWidth === config.minWidth && previous.maxWidth === config.maxWidth && previous.defaultWidth === config.defaultWidth && previous.compactDefaultWidth === config.compactDefaultWidth && (previous.requestedWidth === config.requestedWidth || (current === null || current === void 0 ? void 0 : current.area) === area) ? current : null;
|
|
94
|
+
});
|
|
95
|
+
// Allocate once per registration, outside the updater React may replay. Gaps are
|
|
96
|
+
// harmless: only relative ordering of closed-to-open transitions is significant.
|
|
97
|
+
const openedAt = ++openSequence.current;
|
|
98
|
+
setAreas(current => {
|
|
99
|
+
const existing = current[area];
|
|
100
|
+
return {
|
|
101
|
+
...current,
|
|
102
|
+
[area]: {
|
|
103
|
+
...existing,
|
|
104
|
+
...config,
|
|
105
|
+
liveResize: config.isOpen ? existing === null || existing === void 0 ? void 0 : existing.liveResize : undefined,
|
|
106
|
+
openedAt: config.isOpen && !(existing !== null && existing !== void 0 && existing.isOpen) ? openedAt : existing === null || existing === void 0 ? void 0 : existing.openedAt
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
}, []);
|
|
111
|
+
const unregisterArea = useCallback(area => {
|
|
112
|
+
delete configsRef.current[area];
|
|
113
|
+
setResizeSession(null);
|
|
114
|
+
setAreas(current => {
|
|
115
|
+
if (!current[area]) {
|
|
116
|
+
return current;
|
|
117
|
+
}
|
|
118
|
+
// Closing uses isOpen: false and retains preferences. Unregistering means
|
|
119
|
+
// the owning component unmounted; a replacement must start with its own defaults.
|
|
120
|
+
const next = {
|
|
121
|
+
...current
|
|
122
|
+
};
|
|
123
|
+
delete next[area];
|
|
124
|
+
return next;
|
|
125
|
+
});
|
|
126
|
+
}, []);
|
|
127
|
+
const freezeSiblingWidths = useCallback(area => {
|
|
128
|
+
const baselineWidths = {};
|
|
129
|
+
for (const areaName of ['side-nav', 'panel', 'chat-panel']) {
|
|
130
|
+
const state = areaStatesRef.current[areaName];
|
|
131
|
+
if ((state === null || state === void 0 ? void 0 : state.mode) === 'inline') {
|
|
132
|
+
baselineWidths[areaName] = state.width;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Keep the same siblings fixed after completion, including while a controlled
|
|
136
|
+
// consumer rejects or asynchronously accepts the requested width.
|
|
137
|
+
setResizeSession({
|
|
138
|
+
area,
|
|
139
|
+
baselineWidths
|
|
140
|
+
});
|
|
141
|
+
}, []);
|
|
142
|
+
const startResize = useCallback((area, mode) => {
|
|
143
|
+
if (mode !== 'inline') {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
freezeSiblingWidths(area);
|
|
147
|
+
|
|
148
|
+
// Start from the effective width, which may already be compressed below the preferred width.
|
|
149
|
+
// Reapplying the preferred width here would move the splitter away from the pointer.
|
|
150
|
+
const renderedState = areaStatesRef.current[area];
|
|
151
|
+
if ((renderedState === null || renderedState === void 0 ? void 0 : renderedState.mode) === 'inline') {
|
|
152
|
+
setAreas(current => {
|
|
153
|
+
const existing = current[area];
|
|
154
|
+
return existing ? {
|
|
155
|
+
...current,
|
|
156
|
+
[area]: {
|
|
157
|
+
...existing,
|
|
158
|
+
liveResize: {
|
|
159
|
+
mode,
|
|
160
|
+
width: renderedState.width
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} : current;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}, [freezeSiblingWidths]);
|
|
167
|
+
const resize = useCallback((area, mode, width) => {
|
|
168
|
+
setAreas(current => {
|
|
169
|
+
var _existing$liveResize;
|
|
170
|
+
const existing = current[area];
|
|
171
|
+
if ((existing === null || existing === void 0 ? void 0 : (_existing$liveResize = existing.liveResize) === null || _existing$liveResize === void 0 ? void 0 : _existing$liveResize.mode) === mode && existing.liveResize.width === width) {
|
|
172
|
+
return current;
|
|
173
|
+
}
|
|
174
|
+
return existing ? {
|
|
175
|
+
...current,
|
|
176
|
+
[area]: {
|
|
177
|
+
...existing,
|
|
178
|
+
liveResize: {
|
|
179
|
+
mode,
|
|
180
|
+
width
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} : current;
|
|
184
|
+
});
|
|
185
|
+
}, []);
|
|
186
|
+
const completeResize = useCallback((area, mode, width) => {
|
|
187
|
+
if (mode === 'inline') {
|
|
188
|
+
// Keyboard resizing completes without a preceding pointer-drag session.
|
|
189
|
+
freezeSiblingWidths(area);
|
|
190
|
+
}
|
|
191
|
+
setAreas(current => {
|
|
192
|
+
const existing = current[area];
|
|
193
|
+
if (!existing) {
|
|
194
|
+
return current;
|
|
195
|
+
}
|
|
196
|
+
if (mode === 'overlay') {
|
|
197
|
+
return {
|
|
198
|
+
...current,
|
|
199
|
+
[area]: {
|
|
200
|
+
...existing,
|
|
201
|
+
liveResize: undefined,
|
|
202
|
+
preferredOverlayWidth: width
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
...current,
|
|
208
|
+
[area]: {
|
|
209
|
+
...existing,
|
|
210
|
+
liveResize: undefined,
|
|
211
|
+
preferredInlineWidth: width
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
}, [freezeSiblingWidths]);
|
|
216
|
+
const areaStates = useMemo(() => dimensions === null ? {} : calculateLayoutAreaStates({
|
|
217
|
+
...dimensions,
|
|
218
|
+
occupiedWidth,
|
|
219
|
+
mainMinWidth,
|
|
220
|
+
areas,
|
|
221
|
+
resizeSession
|
|
222
|
+
}), [areas, mainMinWidth, resizeSession, dimensions, occupiedWidth]);
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
areaStatesRef.current = areaStates;
|
|
225
|
+
}, [areaStates]);
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
var _areaStates$sideNav, _areaStates$chatPane, _sideNav$openedAt, _chatPanel$openedAt;
|
|
228
|
+
if (((_areaStates$sideNav = areaStates['side-nav']) === null || _areaStates$sideNav === void 0 ? void 0 : _areaStates$sideNav.mode) !== 'overlay' || ((_areaStates$chatPane = areaStates['chat-panel']) === null || _areaStates$chatPane === void 0 ? void 0 : _areaStates$chatPane.mode) !== 'overlay') {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const sideNav = areas['side-nav'];
|
|
232
|
+
const chatPanel = areas['chat-panel'];
|
|
233
|
+
if (!(sideNav !== null && sideNav !== void 0 && sideNav.isOpen) || !(chatPanel !== null && chatPanel !== void 0 && chatPanel.isOpen)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (((_sideNav$openedAt = sideNav.openedAt) !== null && _sideNav$openedAt !== void 0 ? _sideNav$openedAt : 0) > ((_chatPanel$openedAt = chatPanel.openedAt) !== null && _chatPanel$openedAt !== void 0 ? _chatPanel$openedAt : 0)) {
|
|
237
|
+
var _chatPanel$onRequestC;
|
|
238
|
+
(_chatPanel$onRequestC = chatPanel.onRequestClose) === null || _chatPanel$onRequestC === void 0 ? void 0 : _chatPanel$onRequestC.call(chatPanel);
|
|
239
|
+
} else {
|
|
240
|
+
var _sideNav$onRequestClo;
|
|
241
|
+
(_sideNav$onRequestClo = sideNav.onRequestClose) === null || _sideNav$onRequestClo === void 0 ? void 0 : _sideNav$onRequestClo.call(sideNav);
|
|
242
|
+
}
|
|
243
|
+
}, [areas, areaStates]);
|
|
244
|
+
|
|
245
|
+
// Read bounds lazily: changing a sibling's capacity must not publish new render
|
|
246
|
+
// state or recreate drag listeners for a region whose own width did not change.
|
|
247
|
+
const getResizeBounds = useCallback(area => {
|
|
248
|
+
var _areaStatesRef$curren;
|
|
249
|
+
const bounds = (_areaStatesRef$curren = areaStatesRef.current[area]) === null || _areaStatesRef$curren === void 0 ? void 0 : _areaStatesRef$curren.resizeBounds;
|
|
250
|
+
return bounds ? {
|
|
251
|
+
min: `${bounds.min}px`,
|
|
252
|
+
max: `${bounds.max}px`
|
|
253
|
+
} : undefined;
|
|
254
|
+
}, []);
|
|
255
|
+
const value = useMemo(() => ({
|
|
256
|
+
getResizeBounds,
|
|
257
|
+
registerArea,
|
|
258
|
+
unregisterArea,
|
|
259
|
+
startResize,
|
|
260
|
+
resize,
|
|
261
|
+
completeResize,
|
|
262
|
+
setMainMinWidth
|
|
263
|
+
}), [completeResize, getResizeBounds, registerArea, resize, startResize, unregisterArea]);
|
|
264
|
+
const sideNavState = useStableAreaState(areaStates['side-nav']);
|
|
265
|
+
const panelState = useStableAreaState(areaStates.panel);
|
|
266
|
+
const chatPanelState = useStableAreaState(areaStates['chat-panel']);
|
|
267
|
+
const SideNavStateProvider = layoutAreaStateContexts['side-nav'].Provider;
|
|
268
|
+
const PanelStateProvider = layoutAreaStateContexts.panel.Provider;
|
|
269
|
+
const ChatPanelStateProvider = layoutAreaStateContexts['chat-panel'].Provider;
|
|
270
|
+
return /*#__PURE__*/React.createElement(LayoutAreaSizingContext.Provider, {
|
|
271
|
+
value: value
|
|
272
|
+
}, /*#__PURE__*/React.createElement(SideNavStateProvider, {
|
|
273
|
+
value: sideNavState
|
|
274
|
+
}, /*#__PURE__*/React.createElement(PanelStateProvider, {
|
|
275
|
+
value: panelState
|
|
276
|
+
}, /*#__PURE__*/React.createElement(ChatPanelStateProvider, {
|
|
277
|
+
value: chatPanelState
|
|
278
|
+
}, children))));
|
|
279
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
// Width changes should notify only the affected region, not every sizing consumer.
|
|
3
|
+
export const layoutAreaStateContexts = {
|
|
4
|
+
'side-nav': /*#__PURE__*/createContext(undefined),
|
|
5
|
+
panel: /*#__PURE__*/createContext(undefined),
|
|
6
|
+
'chat-panel': /*#__PURE__*/createContext(undefined)
|
|
7
|
+
};
|
|
@@ -5,7 +5,10 @@ import { ax, ix } from "@compiled/react/runtime";
|
|
|
5
5
|
import { Fragment } from 'react';
|
|
6
6
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
7
7
|
import { useSkipLink } from '../../../context/skip-links/use-skip-link';
|
|
8
|
+
import { mainMinimumWidthVar } from '../constants';
|
|
9
|
+
import { gridRootId } from '../root';
|
|
8
10
|
import { useLayoutId } from '../use-layout-id';
|
|
11
|
+
import { useLayoutMainSizing } from '../use-layout-main-sizing';
|
|
9
12
|
const contentInsetBlockStartStatic = `calc(var(--n_bnrM, 0px) + var(--n_tNvM, 0px))`;
|
|
10
13
|
const contentHeightWhenFixedStatic = `calc(100vh - var(--n_bnrM, 0px) - var(--n_tNvM, 0px))`;
|
|
11
14
|
const mainElementStyles = {
|
|
@@ -20,13 +23,18 @@ export function Main({
|
|
|
20
23
|
xcss,
|
|
21
24
|
skipLinkLabel = fg('platform_dst_nav4_skip_link_a11y_1') ? 'Main content' : 'Main Content',
|
|
22
25
|
testId,
|
|
23
|
-
id: providedId
|
|
26
|
+
id: providedId,
|
|
27
|
+
minWidth = 320
|
|
24
28
|
}) {
|
|
25
29
|
const id = useLayoutId({
|
|
26
30
|
providedId
|
|
27
31
|
});
|
|
32
|
+
useLayoutMainSizing(minWidth);
|
|
28
33
|
useSkipLink(id, skipLinkLabel);
|
|
29
|
-
return /*#__PURE__*/React.createElement(Fragment, null,
|
|
34
|
+
return /*#__PURE__*/React.createElement(Fragment, null, fg('platform-dst-chat-panel-layout') &&
|
|
35
|
+
/*#__PURE__*/
|
|
36
|
+
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-global-styles
|
|
37
|
+
React.createElement("style", null, `#${gridRootId} { ${mainMinimumWidthVar}: ${minWidth}px; }`), /*#__PURE__*/React.createElement("div", {
|
|
30
38
|
id: id,
|
|
31
39
|
"data-layout-slot": true,
|
|
32
40
|
className: ax([mainElementStyles.root, xcss]),
|