Fix Height for Main Layout Inspector
Summary: in Layout plugin, scrollbars where often not visible, for example to see the vertical scrollbar, one had to scroll to the horizontal end first. Also introduced the `Scrollable` component to simplify this in the feature and separate the concepts of rendering something large and making it scrollable. This diff cleans up the layout structure and fixes the problem changelog: Fixed several minor layout issues in the Layout plugin Reviewed By: cekkaewnumchai Differential Revision: D21283157 fbshipit-source-id: 81849151475165796c65001616f038a9d6cbdfb2
This commit is contained in:
committed by
Facebook GitHub Bot
parent
22dfc33da0
commit
b0ab9b9b98
20
desktop/app/src/ui/components/Scrollable.tsx
Normal file
20
desktop/app/src/ui/components/Scrollable.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const Scrollable: React.FC<{children: React.ReactNode}> = styled('div')({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'auto',
|
||||
});
|
||||
Scrollable.displayName = 'Scrollable';
|
||||
|
||||
export default Scrollable;
|
||||
@@ -13,6 +13,7 @@ const VerticalRule = styled.div({
|
||||
backgroundColor: '#c9ced4',
|
||||
width: 3,
|
||||
margin: '0',
|
||||
flexShrink: 0,
|
||||
});
|
||||
VerticalRule.displayName = 'VerticalRule';
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import Text from '../Text';
|
||||
import styled from '@emotion/styled';
|
||||
import {clipboard, MenuItemConstructorOptions} from 'electron';
|
||||
import React, {MouseEvent, KeyboardEvent} from 'react';
|
||||
import {Scrollable} from '../..';
|
||||
|
||||
export const ROW_HEIGHT = 23;
|
||||
|
||||
@@ -424,17 +425,9 @@ const ElementsContainer = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
minHeight: '100%',
|
||||
minWidth: '100%',
|
||||
overflow: 'auto',
|
||||
});
|
||||
ElementsContainer.displayName = 'Elements:ElementsContainer';
|
||||
|
||||
const ElementsBox = styled(FlexColumn)({
|
||||
alignItems: 'flex-start',
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
});
|
||||
ElementsBox.displayName = 'Elements:ElementsBox';
|
||||
|
||||
export type DecorateRow = (e: Element) => ReactElement<any> | undefined | null;
|
||||
|
||||
type ElementsProps = {
|
||||
@@ -710,14 +703,14 @@ export class Elements extends PureComponent<ElementsProps, ElementsState> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ElementsBox>
|
||||
<Scrollable>
|
||||
<ElementsContainer
|
||||
onKeyDown={this.onKeyDown}
|
||||
tabIndex={0}
|
||||
ref={this._outerRef}>
|
||||
{this.state.flatElements.map(this.buildRow)}
|
||||
</ElementsContainer>
|
||||
</ElementsBox>
|
||||
</Scrollable>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,4 +176,5 @@ export {default as Info} from './components/Info';
|
||||
export {default as Bordered} from './components/Bordered';
|
||||
export {default as AlternatingRows} from './components/AlternatingRows';
|
||||
export {default as Layout} from './components/Layout';
|
||||
export {default as Scrollable} from './components/Scrollable';
|
||||
export * from './components/Highlight';
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
Element,
|
||||
ElementSearchResultSet,
|
||||
PluginClient,
|
||||
FlexColumn,
|
||||
FlexRow,
|
||||
FlipperPlugin,
|
||||
Toolbar,
|
||||
@@ -29,6 +28,7 @@ import {
|
||||
ReduxState,
|
||||
ArchivedDevice,
|
||||
ToolbarIcon,
|
||||
Layout,
|
||||
} from 'flipper';
|
||||
import Inspector from './Inspector';
|
||||
import InspectorSidebar from './InspectorSidebar';
|
||||
@@ -80,7 +80,11 @@ const FlipperADButton = styled(Button)({
|
||||
type ClientGetNodesCalls = 'getNodes' | 'getAXNodes';
|
||||
type ClientMethodCalls = 'getRoot' | 'getAXRoot' | ClientGetNodesCalls;
|
||||
|
||||
export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
export default class LayoutPlugin extends FlipperPlugin<
|
||||
State,
|
||||
any,
|
||||
PersistedState
|
||||
> {
|
||||
FlipperADBar() {
|
||||
return (
|
||||
<FlipperADBarContainer>
|
||||
@@ -122,7 +126,7 @@ export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
|
||||
if (rootElement) {
|
||||
statusUpdate && statusUpdate('Fetching Child Nodes...');
|
||||
await Layout.getAllNodes(
|
||||
await LayoutPlugin.getAllNodes(
|
||||
rootElement,
|
||||
elements,
|
||||
callClient,
|
||||
@@ -133,7 +137,7 @@ export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
const AXelements: ElementMap = {};
|
||||
if (rootAXElement) {
|
||||
statusUpdate && statusUpdate('Fetching Child AX Nodes...');
|
||||
await Layout.getAllNodes(
|
||||
await LayoutPlugin.getAllNodes(
|
||||
rootAXElement,
|
||||
AXelements,
|
||||
callClient,
|
||||
@@ -167,7 +171,7 @@ export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
async ({elements}: {elements: Array<Element>}) => {
|
||||
await Promise.all(
|
||||
elements.map(async (elem) => {
|
||||
await Layout.getAllNodes(
|
||||
await LayoutPlugin.getAllNodes(
|
||||
elem,
|
||||
nodeMap,
|
||||
callClient,
|
||||
@@ -333,7 +337,7 @@ export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
ax: this.state.inAXMode,
|
||||
});
|
||||
};
|
||||
showFlipperADBar: boolean = false;
|
||||
showFlipperADBar: boolean = true;
|
||||
|
||||
getScreenDimensions(): {width: number; height: number} | null {
|
||||
if (this.state.screenDimensions) {
|
||||
@@ -405,115 +409,118 @@ export default class Layout extends FlipperPlugin<State, any, PersistedState> {
|
||||
/>
|
||||
);
|
||||
|
||||
const axInspector = this.state.inAXMode && (
|
||||
<Inspector
|
||||
{...inspectorProps}
|
||||
onSelect={(selectedAXElement) => this.setState({selectedAXElement})}
|
||||
showsSidebar={true}
|
||||
ax
|
||||
/>
|
||||
);
|
||||
|
||||
const divider = this.state.inAXMode && <VerticalRule />;
|
||||
const axInspector = this.state.inAXMode ? (
|
||||
<FlexRow>
|
||||
<VerticalRule />
|
||||
<Inspector
|
||||
{...inspectorProps}
|
||||
onSelect={(selectedAXElement) => this.setState({selectedAXElement})}
|
||||
showsSidebar={true}
|
||||
ax
|
||||
/>
|
||||
</FlexRow>
|
||||
) : null;
|
||||
|
||||
const showAnalyzeYogaPerformanceButton = GK.get('flipper_yogaperformance');
|
||||
|
||||
const screenDimensions = this.getScreenDimensions();
|
||||
|
||||
if (!this.state.init) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<FlexColumn grow={true}>
|
||||
{this.state.init && (
|
||||
<>
|
||||
<Toolbar>
|
||||
{!this.props.isArchivedDevice && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleTargetMode}
|
||||
title="Toggle target mode"
|
||||
icon="target"
|
||||
active={this.state.inTargetMode}
|
||||
/>
|
||||
)}
|
||||
{this.realClient.query.os === 'Android' && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleAXMode}
|
||||
title="Toggle to see the accessibility hierarchy"
|
||||
icon="accessibility"
|
||||
active={this.state.inAXMode}
|
||||
/>
|
||||
)}
|
||||
{!this.props.isArchivedDevice && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleAlignmentMode}
|
||||
title="Toggle AlignmentMode to show alignment lines"
|
||||
icon="borders"
|
||||
active={this.state.inAlignmentMode}
|
||||
/>
|
||||
)}
|
||||
{this.props.isArchivedDevice &&
|
||||
this.state.visualizerScreenshot && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleVisualizer}
|
||||
title="Toggle visual recreation of layout"
|
||||
icon="mobile"
|
||||
active={!!this.state.visualizerWindow}
|
||||
/>
|
||||
)}
|
||||
<>
|
||||
<Layout.Top>
|
||||
<Toolbar>
|
||||
{!this.props.isArchivedDevice && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleTargetMode}
|
||||
title="Toggle target mode"
|
||||
icon="target"
|
||||
active={this.state.inTargetMode}
|
||||
/>
|
||||
)}
|
||||
{this.realClient.query.os === 'Android' && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleAXMode}
|
||||
title="Toggle to see the accessibility hierarchy"
|
||||
icon="accessibility"
|
||||
active={this.state.inAXMode}
|
||||
/>
|
||||
)}
|
||||
{!this.props.isArchivedDevice && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleAlignmentMode}
|
||||
title="Toggle AlignmentMode to show alignment lines"
|
||||
icon="borders"
|
||||
active={this.state.inAlignmentMode}
|
||||
/>
|
||||
)}
|
||||
{this.props.isArchivedDevice && this.state.visualizerScreenshot && (
|
||||
<ToolbarIcon
|
||||
onClick={this.onToggleVisualizer}
|
||||
title="Toggle visual recreation of layout"
|
||||
icon="mobile"
|
||||
active={!!this.state.visualizerWindow}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Search
|
||||
client={this.getClient()}
|
||||
setPersistedState={this.props.setPersistedState}
|
||||
persistedState={this.props.persistedState}
|
||||
onSearchResults={(searchResults) =>
|
||||
this.setState({searchResults})
|
||||
}
|
||||
inAXMode={this.state.inAXMode}
|
||||
initialQuery={this.props.deepLinkPayload}
|
||||
/>
|
||||
</Toolbar>
|
||||
<FlexRow grow={true}>
|
||||
<Search
|
||||
client={this.getClient()}
|
||||
setPersistedState={this.props.setPersistedState}
|
||||
persistedState={this.props.persistedState}
|
||||
onSearchResults={(searchResults) =>
|
||||
this.setState({searchResults})
|
||||
}
|
||||
inAXMode={this.state.inAXMode}
|
||||
initialQuery={this.props.deepLinkPayload}
|
||||
/>
|
||||
</Toolbar>
|
||||
<Layout.Bottom>
|
||||
<Layout.Right>
|
||||
{inspector}
|
||||
{divider}
|
||||
{axInspector}
|
||||
</FlexRow>
|
||||
{this.showFlipperADBar && this.FlipperADBar()}
|
||||
<DetailSidebar>
|
||||
<InspectorSidebar
|
||||
client={this.getClient()}
|
||||
realClient={this.realClient}
|
||||
element={element}
|
||||
onValueChanged={this.onDataValueChanged}
|
||||
logger={this.props.logger}
|
||||
/>
|
||||
{showAnalyzeYogaPerformanceButton &&
|
||||
element &&
|
||||
element.decoration === 'litho' ? (
|
||||
<Button
|
||||
icon={'share-external'}
|
||||
compact={true}
|
||||
style={{marginTop: 8, marginRight: 12}}
|
||||
onClick={() => {
|
||||
this.props.selectPlugin('YogaPerformance', element!.id);
|
||||
}}>
|
||||
Analyze Yoga Performance
|
||||
</Button>
|
||||
) : null}
|
||||
</DetailSidebar>
|
||||
{this.state.visualizerWindow &&
|
||||
screenDimensions &&
|
||||
(this.state.visualizerScreenshot ? (
|
||||
<VisualizerPortal
|
||||
container={this.state.visualizerWindow.document.body}
|
||||
elements={this.props.persistedState.elements}
|
||||
highlightedElement={this.state.highlightedElement}
|
||||
screenshotURL={this.state.visualizerScreenshot}
|
||||
screenDimensions={screenDimensions}
|
||||
/>
|
||||
) : (
|
||||
'Loading...'
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</FlexColumn>
|
||||
</Layout.Right>
|
||||
{this.showFlipperADBar ? this.FlipperADBar() : null}
|
||||
</Layout.Bottom>
|
||||
</Layout.Top>
|
||||
|
||||
<DetailSidebar>
|
||||
<InspectorSidebar
|
||||
client={this.getClient()}
|
||||
realClient={this.realClient}
|
||||
element={element}
|
||||
onValueChanged={this.onDataValueChanged}
|
||||
logger={this.props.logger}
|
||||
/>
|
||||
{showAnalyzeYogaPerformanceButton &&
|
||||
element &&
|
||||
element.decoration === 'litho' ? (
|
||||
<Button
|
||||
icon={'share-external'}
|
||||
compact={true}
|
||||
style={{marginTop: 8, marginRight: 12}}
|
||||
onClick={() => {
|
||||
this.props.selectPlugin('YogaPerformance', element!.id);
|
||||
}}>
|
||||
Analyze Yoga Performance
|
||||
</Button>
|
||||
) : null}
|
||||
</DetailSidebar>
|
||||
{this.state.visualizerWindow &&
|
||||
screenDimensions &&
|
||||
(this.state.visualizerScreenshot ? (
|
||||
<VisualizerPortal
|
||||
container={this.state.visualizerWindow.document.body}
|
||||
elements={this.props.persistedState.elements}
|
||||
highlightedElement={this.state.highlightedElement}
|
||||
screenshotURL={this.state.visualizerScreenshot}
|
||||
screenDimensions={screenDimensions}
|
||||
/>
|
||||
) : (
|
||||
'Loading...'
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user