Scrolling improvements
Summary: Split container had a convenient property `scrollable`, that automatically applies a Scroll container to the main content. But I noticed it leads to bad design choices because it is so convenient. So sometimes scrolling would be unnecessarily in two directions because of this. Or, since the scroll container wraps around the whole content, toolbars would scroll out of view. By forcing scrolling to be put explicitly in the component tree, we encourage plugin developers to think about where they actually want to have that scroll, and in which direction. Also added options to use the Container padding properties on ScrollContainer, which is great since we can keep the scrollbars outside the padding, and apply it to the content only, to prevent an accidental mistake where people would put a scroll container in a padded container, that would put the scrollbar inside the padding. Reviewed By: cekkaewnumchai Differential Revision: D24502546 fbshipit-source-id: 524004a1c5f33a185f9b959251b72875dd623cb3
This commit is contained in:
committed by
Facebook GitHub Bot
parent
30f5f0b59a
commit
5731e3a155
@@ -92,7 +92,7 @@ export function ConsoleLogs() {
|
|||||||
const styles = useMemo(() => buildTheme(isSandy), [isSandy]);
|
const styles = useMemo(() => buildTheme(isSandy), [isSandy]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Top scrollable>
|
<Layout.Top>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button onClick={clearLogs} icon="trash">
|
<Button onClick={clearLogs} icon="trash">
|
||||||
@@ -101,12 +101,14 @@ export function ConsoleLogs() {
|
|||||||
<Button dropdown={dropdown}>Log Levels</Button>
|
<Button dropdown={dropdown}>Log Levels</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
<Layout.ScrollContainer vertical>
|
||||||
<Console
|
<Console
|
||||||
logs={logs}
|
logs={logs}
|
||||||
filter={logLevels}
|
filter={logLevels}
|
||||||
variant={isDarkMode || !isSandy ? 'dark' : 'light'}
|
variant={isDarkMode || !isSandy ? 'dark' : 'light'}
|
||||||
styles={styles}
|
styles={styles}
|
||||||
/>
|
/>
|
||||||
|
</Layout.ScrollContainer>
|
||||||
</Layout.Top>
|
</Layout.Top>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,11 @@ const demos: PreviewProps[] = [
|
|||||||
'boolean',
|
'boolean',
|
||||||
'specifies in which directions the container should scroll. If none is specified the container will scroll in both directions',
|
'specifies in which directions the container should scroll. If none is specified the container will scroll in both directions',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'padv / padh / pad',
|
||||||
|
'see Container',
|
||||||
|
'Padding will be applied to the child',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
demos: {
|
demos: {
|
||||||
'Basic usage': (
|
'Basic usage': (
|
||||||
@@ -246,34 +251,34 @@ const demos: PreviewProps[] = [
|
|||||||
{aFixedHeightBox}
|
{aFixedHeightBox}
|
||||||
</Layout.Bottom>
|
</Layout.Bottom>
|
||||||
),
|
),
|
||||||
'Layout.Top + scrollable': (
|
'Layout.Top + Layout.ScrollContainer': (
|
||||||
<Layout.Container style={{height: 150}}>
|
<Layout.Container style={{height: 150}}>
|
||||||
<Layout.Top scrollable>
|
<Layout.Top>
|
||||||
{aFixedHeightBox}
|
{aFixedHeightBox}
|
||||||
{largeChild}
|
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||||
</Layout.Top>
|
</Layout.Top>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
),
|
),
|
||||||
'Layout.Left + scrollable': (
|
'Layout.Left + Layout.ScrollContainer': (
|
||||||
<Layout.Container style={{height: 150}}>
|
<Layout.Container style={{height: 150}}>
|
||||||
<Layout.Left scrollable>
|
<Layout.Left>
|
||||||
{aFixedWidthBox}
|
{aFixedWidthBox}
|
||||||
{largeChild}
|
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||||
</Layout.Left>
|
</Layout.Left>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
),
|
),
|
||||||
'Layout.Right + scrollable': (
|
'Layout.Right + Layout.ScrollContainer': (
|
||||||
<Layout.Container style={{height: 150}}>
|
<Layout.Container style={{height: 150}}>
|
||||||
<Layout.Right scrollable>
|
<Layout.Right>
|
||||||
{largeChild}
|
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||||
{aFixedWidthBox}
|
{aFixedWidthBox}
|
||||||
</Layout.Right>
|
</Layout.Right>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
),
|
),
|
||||||
'Layout.Bottom + scrollable': (
|
'Layout.Bottom + Layout.ScrollContainer': (
|
||||||
<Layout.Container style={{height: 150}}>
|
<Layout.Container style={{height: 150}}>
|
||||||
<Layout.Bottom scrollable>
|
<Layout.Bottom>
|
||||||
{largeChild}
|
<Layout.ScrollContainer>{largeChild}</Layout.ScrollContainer>
|
||||||
{aFixedHeightBox}
|
{aFixedHeightBox}
|
||||||
</Layout.Bottom>
|
</Layout.Bottom>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ import {Button, Tooltip, Typography} from 'antd';
|
|||||||
import {InfoCircleOutlined} from '@ant-design/icons';
|
import {InfoCircleOutlined} from '@ant-design/icons';
|
||||||
|
|
||||||
export const LeftSidebar: React.FC = ({children}) => (
|
export const LeftSidebar: React.FC = ({children}) => (
|
||||||
<Layout.Container borderRight padv="small" grow shrink>
|
<Layout.Container
|
||||||
|
borderRight
|
||||||
|
style={{paddingTop: theme.space.small}}
|
||||||
|
grow
|
||||||
|
shrink>
|
||||||
{children}
|
{children}
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -57,15 +57,13 @@ export function AppInspect() {
|
|||||||
</Layout.Horizontal>
|
</Layout.Horizontal>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
<Layout.Container padv={theme.space.large}>
|
<Layout.ScrollContainer vertical padv={theme.space.large}>
|
||||||
<Layout.ScrollContainer vertical>
|
|
||||||
{selectedDevice ? (
|
{selectedDevice ? (
|
||||||
<PluginList />
|
<PluginList />
|
||||||
) : (
|
) : (
|
||||||
<Alert message="No device or app selected" type="info" />
|
<Alert message="No device or app selected" type="info" />
|
||||||
)}
|
)}
|
||||||
</Layout.ScrollContainer>
|
</Layout.ScrollContainer>
|
||||||
</Layout.Container>
|
|
||||||
</Layout.Top>
|
</Layout.Top>
|
||||||
</LeftSidebar>
|
</LeftSidebar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -116,25 +116,26 @@ const ScrollContainer = ({
|
|||||||
children,
|
children,
|
||||||
horizontal,
|
horizontal,
|
||||||
vertical,
|
vertical,
|
||||||
|
padv,
|
||||||
|
padh,
|
||||||
|
pad,
|
||||||
...rest
|
...rest
|
||||||
}: React.HTMLAttributes<HTMLDivElement> & {
|
}: React.HTMLAttributes<HTMLDivElement> & {
|
||||||
horizontal?: boolean;
|
horizontal?: boolean;
|
||||||
vertical?: boolean;
|
vertical?: boolean;
|
||||||
}) => {
|
} & PaddingProps) => {
|
||||||
const axis =
|
const axis =
|
||||||
horizontal && !vertical ? 'x' : !horizontal && vertical ? 'y' : 'both';
|
horizontal && !vertical ? 'x' : !horizontal && vertical ? 'y' : 'both';
|
||||||
return (
|
return (
|
||||||
<ScrollParent axis={axis} {...rest}>
|
<ScrollParent axis={axis} {...rest}>
|
||||||
<ScrollChild axis={axis}>{children}</ScrollChild>
|
<ScrollChild axis={axis} padv={padv} padh={padh} pad={pad}>
|
||||||
|
{children}
|
||||||
|
</ScrollChild>
|
||||||
</ScrollParent>
|
</ScrollParent>
|
||||||
) as any;
|
) as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SplitLayoutProps = {
|
type SplitLayoutProps = {
|
||||||
/**
|
|
||||||
* If set, the dynamically sized pane will get scrollbars when needed
|
|
||||||
*/
|
|
||||||
scrollable?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* If set, items will be centered over the orthogonal direction, if false (the default) items will be stretched.
|
* If set, items will be centered over the orthogonal direction, if false (the default) items will be stretched.
|
||||||
*/
|
*/
|
||||||
@@ -150,8 +151,7 @@ function renderSplitLayout(
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const isSandy = useIsSandy();
|
const isSandy = useIsSandy();
|
||||||
if (!isSandy) return renderLayout(props, direction === 'row', grow === 1);
|
if (!isSandy) return renderLayout(props, direction === 'row', grow === 1);
|
||||||
let [child1, child2] = props.children;
|
const [child1, child2] = props.children;
|
||||||
if (props.scrollable) child2 = <ScrollContainer>{child2}</ScrollContainer>;
|
|
||||||
return (
|
return (
|
||||||
<SandySplitContainer {...props} flexDirection={direction} grow={grow}>
|
<SandySplitContainer {...props} flexDirection={direction} grow={grow}>
|
||||||
{child1}
|
{child1}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export function Component() {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
<Layout.Top scrollable={false}>
|
<Layout.Top>
|
||||||
<Sidebar position="top" minHeight={80} height={80}>
|
<Sidebar position="top" minHeight={80} height={80}>
|
||||||
<EventTable
|
<EventTable
|
||||||
generations={filteredGenerations}
|
generations={filteredGenerations}
|
||||||
@@ -270,7 +270,7 @@ export function Component() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<Layout.Top scrollable={true}>
|
<Layout.Top>
|
||||||
<Sidebar position="top" minHeight={400} height={400}>
|
<Sidebar position="top" minHeight={400} height={400}>
|
||||||
<TreeContainer>
|
<TreeContainer>
|
||||||
<TreeHierarchy
|
<TreeHierarchy
|
||||||
@@ -282,12 +282,14 @@ export function Component() {
|
|||||||
</TreeContainer>
|
</TreeContainer>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
{focusedTreeGeneration && (
|
{focusedTreeGeneration && (
|
||||||
|
<Layout.ScrollContainer>
|
||||||
<StackTrace
|
<StackTrace
|
||||||
data={focusedTreeGeneration.stack_trace}
|
data={focusedTreeGeneration.stack_trace}
|
||||||
skipStackTraceFormat={
|
skipStackTraceFormat={
|
||||||
focusedTreeGeneration.skip_stack_trace_format
|
focusedTreeGeneration.skip_stack_trace_format
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
</Layout.ScrollContainer>
|
||||||
)}
|
)}
|
||||||
</Layout.Top>
|
</Layout.Top>
|
||||||
</Layout.Top>
|
</Layout.Top>
|
||||||
|
|||||||
Reference in New Issue
Block a user