Some fixes in rendering legacy plugins
Summary: Some exploratory testing on all iOS and Android plugins, to see how they behave inside Sandy, and fixed some layout glitches (some were also present without Sandy) General fixes: * Introduced some niceties like searchbox resizing properly, and toolbars wrapping automatically in Sandy, rather than buttons becoming invisible * Containers don't grow anymore by default, but take size of contents * ScrollContainer child is now a Layout.Vertical. Layout.Vertical should be used as default container everywhere (e.g. Tabs, Panels) in the future * Fixed layout issue if a split container had only 1 visible child * DetailsSidebar now scrolls vertically by default * Details sidebar would sometimes render content in-place rather than in the reserved area * AppSelector dropdown and Plugin list will now properly ellipse (...) if there is not enough space Plugin fixes: * Long database / table names in Database plugin would break layout Also fixes https://github.com/facebook/flipper/issues/1611 Reviewed By: passy Differential Revision: D24454188 fbshipit-source-id: c60c867270900a1d4f28587d47067c6ec1072ede
This commit is contained in:
committed by
Facebook GitHub Bot
parent
4f7294c96d
commit
966d748ace
@@ -49,6 +49,7 @@ import {ToggleButton, SmallText, Layout} from './ui';
|
|||||||
import {SandyPluginRenderer} from 'flipper-plugin';
|
import {SandyPluginRenderer} from 'flipper-plugin';
|
||||||
import {isDevicePluginDefinition} from './utils/pluginUtils';
|
import {isDevicePluginDefinition} from './utils/pluginUtils';
|
||||||
import ArchivedDevice from './devices/ArchivedDevice';
|
import ArchivedDevice from './devices/ArchivedDevice';
|
||||||
|
import {ContentContainer} from './sandy-chrome/ContentContainer';
|
||||||
|
|
||||||
const Container = styled(FlexColumn)({
|
const Container = styled(FlexColumn)({
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -436,7 +437,7 @@ class PluginContainer extends PureComponent<Props, State> {
|
|||||||
heading={`Plugin "${
|
heading={`Plugin "${
|
||||||
activePlugin.title || 'Unknown'
|
activePlugin.title || 'Unknown'
|
||||||
}" encountered an error during render`}>
|
}" encountered an error during render`}>
|
||||||
{pluginElement}
|
<ContentContainer>{pluginElement}</ContentContainer>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<SidebarContainer id="detailsSidebar" />
|
<SidebarContainer id="detailsSidebar" />
|
||||||
</Layout.Right>
|
</Layout.Right>
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useEffect, useMemo, useContext} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import {ReactReduxContext} from 'react-redux';
|
|
||||||
import Sidebar from '../ui/components/Sidebar';
|
import Sidebar from '../ui/components/Sidebar';
|
||||||
import {toggleRightSidebarAvailable} from '../reducers/application';
|
import {toggleRightSidebarAvailable} from '../reducers/application';
|
||||||
import {useDispatch, useStore} from '../utils/useStore';
|
import {useDispatch, useStore} from '../utils/useStore';
|
||||||
import {useIsSandy} from '../sandy-chrome/SandyContext';
|
import {useIsSandy} from '../sandy-chrome/SandyContext';
|
||||||
import {ContentContainer} from '../sandy-chrome/ContentContainer';
|
import {ContentContainer} from '../sandy-chrome/ContentContainer';
|
||||||
|
import {Layout} from '../ui';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children: any;
|
children: any;
|
||||||
@@ -24,12 +24,13 @@ type OwnProps = {
|
|||||||
|
|
||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
||||||
const reduxContext = useContext(ReactReduxContext);
|
const [domNode, setDomNode] = useState(
|
||||||
const domNode = useMemo(() => document.getElementById('detailsSidebar'), []);
|
document.getElementById('detailsSidebar'),
|
||||||
|
);
|
||||||
|
|
||||||
if (!reduxContext || !domNode) {
|
if (typeof jest !== 'undefined') {
|
||||||
// For unit tests, make sure to render elements inline
|
// For unit tests, make sure to render elements inline
|
||||||
return <div id="detailsSidebar">{children}</div>;
|
return <div>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSandy = useIsSandy();
|
const isSandy = useIsSandy();
|
||||||
@@ -49,6 +50,19 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
|||||||
[children, rightSidebarAvailable, dispatch],
|
[children, rightSidebarAvailable, dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If the plugin container is mounting and rendering a sidbar immediately, the domNode might not yet be available
|
||||||
|
useEffect(() => {
|
||||||
|
if (!domNode) {
|
||||||
|
const newDomNode = document.getElementById('detailsSidebar');
|
||||||
|
if (!newDomNode) {
|
||||||
|
// if after layouting domNode is still not available, something is wrong...
|
||||||
|
console.error('Failed to obtain detailsSidebar node');
|
||||||
|
} else {
|
||||||
|
setDomNode(newDomNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [domNode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(children &&
|
(children &&
|
||||||
rightSidebarVisible &&
|
rightSidebarVisible &&
|
||||||
@@ -59,7 +73,15 @@ export default function DetailSidebar({children, width, minWidth}: OwnProps) {
|
|||||||
width={width || 300}
|
width={width || 300}
|
||||||
position="right"
|
position="right"
|
||||||
gutter={isSandy}>
|
gutter={isSandy}>
|
||||||
{isSandy ? <ContentContainer>{children}</ContentContainer> : children}
|
{isSandy ? (
|
||||||
|
<ContentContainer>
|
||||||
|
<Layout.ScrollContainer vertical>
|
||||||
|
{children}
|
||||||
|
</Layout.ScrollContainer>
|
||||||
|
</ContentContainer>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
</Sidebar>,
|
</Sidebar>,
|
||||||
domNode,
|
domNode,
|
||||||
)) ||
|
)) ||
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ exports[`load PluginInstaller list 1`] = `
|
|||||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="css-awcbnc-View-FlexBox-SearchBox e271nro1"
|
class="css-1dulget-View-FlexBox-SearchBox e271nro1"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
class="css-mquw9q-Input-SearchInput e271nro2"
|
class="css-184o3nx-Input-SearchInput e271nro2"
|
||||||
placeholder="Search Flipper plugins..."
|
placeholder="Search Flipper plugins..."
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
@@ -304,10 +304,10 @@ exports[`load PluginInstaller list with one plugin installed 1`] = `
|
|||||||
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
class="css-1qqef1i-View-FlexBox-FlexRow-ToolbarContainer e13mj6h80"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="css-awcbnc-View-FlexBox-SearchBox e271nro1"
|
class="css-1dulget-View-FlexBox-SearchBox e271nro1"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
class="css-mquw9q-Input-SearchInput e271nro2"
|
class="css-184o3nx-Input-SearchInput e271nro2"
|
||||||
placeholder="Search Flipper plugins..."
|
placeholder="Search Flipper plugins..."
|
||||||
type="text"
|
type="text"
|
||||||
value=""
|
value=""
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export type Action =
|
|||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: 'SELECT_CLIENT';
|
type: 'SELECT_CLIENT';
|
||||||
payload: string;
|
payload: string | null;
|
||||||
}
|
}
|
||||||
| RegisterPluginAction
|
| RegisterPluginAction
|
||||||
| {
|
| {
|
||||||
@@ -404,7 +404,7 @@ export const starPlugin = (payload: {
|
|||||||
payload,
|
payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectClient = (clientId: string): Action => ({
|
export const selectClient = (clientId: string | null): Action => ({
|
||||||
type: 'SELECT_CLIENT',
|
type: 'SELECT_CLIENT',
|
||||||
payload: clientId,
|
payload: clientId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {Layout, styled} from '../ui';
|
|||||||
import {theme} from './theme';
|
import {theme} from './theme';
|
||||||
|
|
||||||
export const ContentContainer = styled(Layout.Container)({
|
export const ContentContainer = styled(Layout.Container)({
|
||||||
|
flex: 1,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
background: theme.backgroundDefault,
|
background: theme.backgroundDefault,
|
||||||
border: `1px solid ${theme.dividerColor}`,
|
border: `1px solid ${theme.dividerColor}`,
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ 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">
|
<Layout.Vertical borderRight padv="small" grow shrink>
|
||||||
{children}
|
{children}
|
||||||
</Layout.Container>
|
</Layout.Vertical>
|
||||||
);
|
);
|
||||||
|
|
||||||
export function SidebarTitle({
|
export function SidebarTitle({
|
||||||
|
|||||||
@@ -63,7 +63,13 @@ export function AppSelector() {
|
|||||||
const client = clients.find((client) => client.id === selectedApp);
|
const client = clients.find((client) => client.id === selectedApp);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Radio.Group value={selectedApp} size="small">
|
<Radio.Group
|
||||||
|
value={selectedApp}
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: 1,
|
||||||
|
}}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={
|
overlay={
|
||||||
<Menu selectedKeys={selectedApp ? [selectedApp] : []}>
|
<Menu selectedKeys={selectedApp ? [selectedApp] : []}>
|
||||||
@@ -73,7 +79,7 @@ export function AppSelector() {
|
|||||||
<AppInspectButton title="Select the device / app to inspect">
|
<AppInspectButton title="Select the device / app to inspect">
|
||||||
<Layout.Horizontal gap center>
|
<Layout.Horizontal gap center>
|
||||||
<AppIcon appname={client?.query.app} />
|
<AppIcon appname={client?.query.app} />
|
||||||
<Layout.Vertical>
|
<Layout.Vertical grow shrink>
|
||||||
<Text strong>{client?.query.app ?? ''}</Text>
|
<Text strong>{client?.query.app ?? ''}</Text>
|
||||||
<Text>
|
<Text>
|
||||||
{selectedDevice?.displayTitle() || 'Available devices'}
|
{selectedDevice?.displayTitle() || 'Available devices'}
|
||||||
@@ -91,8 +97,9 @@ const AppInspectButton = styled(Button)({
|
|||||||
background: theme.backgroundTransparentHover,
|
background: theme.backgroundTransparentHover,
|
||||||
height: 52,
|
height: 52,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
width: '100%',
|
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
|
flex: `1 1 0`,
|
||||||
|
overflow: 'hidden', // required for ellipsis
|
||||||
paddingLeft: theme.space.small,
|
paddingLeft: theme.space.small,
|
||||||
paddingRight: theme.space.small,
|
paddingRight: theme.space.small,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
@@ -128,7 +135,10 @@ function computeEntries(
|
|||||||
key={device.serial}
|
key={device.serial}
|
||||||
style={{fontWeight: 'bold'}}
|
style={{fontWeight: 'bold'}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
dispatch(selectDevice(device));
|
batch(() => {
|
||||||
|
dispatch(selectDevice(device));
|
||||||
|
dispatch(selectClient(null));
|
||||||
|
});
|
||||||
}}>
|
}}>
|
||||||
{device.displayTitle()}
|
{device.displayTitle()}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|||||||
@@ -454,6 +454,10 @@ export function computePluginLists(
|
|||||||
const PluginMenu = styled(Menu)({
|
const PluginMenu = styled(Menu)({
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
|
'.ant-typography': {
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
},
|
||||||
'.ant-menu-inline .ant-menu-item, .ant-menu-inline .ant-menu-submenu-title ': {
|
'.ant-menu-inline .ant-menu-item, .ant-menu-inline .ant-menu-submenu-title ': {
|
||||||
width: '100%', // reset to remove weird bonus pixel from ANT
|
width: '100%', // reset to remove weird bonus pixel from ANT
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ type ContainerProps = {
|
|||||||
rounded?: boolean;
|
rounded?: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
// grow to available space?
|
||||||
|
grow?: boolean;
|
||||||
|
// allow shrinking beyond minally needed size? Makes using ellipsis on children possible
|
||||||
|
shrink?: boolean;
|
||||||
} & PaddingProps;
|
} & PaddingProps;
|
||||||
|
|
||||||
const Container = styled.div<ContainerProps>(
|
const Container = styled.div<ContainerProps>(
|
||||||
@@ -43,17 +47,19 @@ const Container = styled.div<ContainerProps>(
|
|||||||
rounded,
|
rounded,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
grow,
|
||||||
|
shrink,
|
||||||
...rest
|
...rest
|
||||||
}) => ({
|
}) => ({
|
||||||
boxSizing: 'border-box',
|
|
||||||
minWidth: `0`, // ensures the Container can shrink smaller than it's largest
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
flex: grow && shrink ? `1 1 0` : grow ? `1 0 auto` : shrink ? `0 1 0` : 0,
|
||||||
|
minWidth: shrink ? 0 : undefined,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
padding: normalizePadding(rest),
|
padding: normalizePadding(rest),
|
||||||
borderRadius: rounded ? theme.containerBorderRadius : undefined,
|
borderRadius: rounded ? theme.containerBorderRadius : undefined,
|
||||||
flex: 1,
|
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
borderColor: theme.dividerColor,
|
borderColor: theme.dividerColor,
|
||||||
borderWidth: bordered
|
borderWidth: bordered
|
||||||
@@ -64,6 +70,34 @@ const Container = styled.div<ContainerProps>(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
type DistributionProps = ContainerProps & {
|
||||||
|
/**
|
||||||
|
* Gab between individual items
|
||||||
|
*/
|
||||||
|
gap?: Spacing;
|
||||||
|
/**
|
||||||
|
* If set, items will be aligned in the center, if false (the default) items will be stretched.
|
||||||
|
*/
|
||||||
|
center?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
function distributionStyle({gap, center}: DistributionProps) {
|
||||||
|
return {
|
||||||
|
gap: normalizeSpace(gap, theme.space.small),
|
||||||
|
alignItems: center ? 'center' : 'stretch',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const Horizontal = styled(Container)<DistributionProps>((props) => ({
|
||||||
|
...distributionStyle(props),
|
||||||
|
flexDirection: 'row',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Vertical = styled(Container)<DistributionProps>((props) => ({
|
||||||
|
...distributionStyle(props),
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
|
const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
@@ -72,7 +106,7 @@ const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
|
|||||||
overflowY: axis === 'x' ? 'hidden' : 'auto',
|
overflowY: axis === 'x' ? 'hidden' : 'auto',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ScrollChild = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
|
const ScrollChild = styled(Vertical)<{axis?: ScrollAxis}>(({axis}) => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
minHeight: '100%',
|
minHeight: '100%',
|
||||||
minWidth: '100%',
|
minWidth: '100%',
|
||||||
@@ -100,32 +134,6 @@ const ScrollContainer = ({
|
|||||||
) as any;
|
) as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DistributionProps = ContainerProps & {
|
|
||||||
/**
|
|
||||||
* Gab between individual items
|
|
||||||
*/
|
|
||||||
gap?: Spacing;
|
|
||||||
/**
|
|
||||||
* If set, items will be aligned in the center, if false (the default) items will be stretched.
|
|
||||||
*/
|
|
||||||
center?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Horizontal = styled(Container)<DistributionProps>(({gap, center}) => ({
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: normalizeSpace(gap, theme.space.small),
|
|
||||||
alignItems: center ? 'center' : 'stretch',
|
|
||||||
minWidth: 'auto', // corrects 0 on Container
|
|
||||||
}));
|
|
||||||
|
|
||||||
const Vertical = styled(Container)<DistributionProps>(({gap, center}) => ({
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: normalizeSpace(gap, theme.space.small),
|
|
||||||
alignItems: center ? 'center' : 'stretch',
|
|
||||||
}));
|
|
||||||
|
|
||||||
type SplitLayoutProps = {
|
type SplitLayoutProps = {
|
||||||
/**
|
/**
|
||||||
* If set, the dynamically sized pane will get scrollbars when needed
|
* If set, the dynamically sized pane will get scrollbars when needed
|
||||||
@@ -219,10 +227,10 @@ const SandySplitContainer = styled.div<{
|
|||||||
flexDirection: props.flexDirection,
|
flexDirection: props.flexDirection,
|
||||||
alignItems: props.center ? 'center' : 'stretch',
|
alignItems: props.center ? 'center' : 'stretch',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
'> :first-child': {
|
'> :nth-child(1)': {
|
||||||
flex: props.grow === 1 ? growStyle : fixedStyle,
|
flex: props.grow === 1 ? growStyle : fixedStyle,
|
||||||
},
|
},
|
||||||
'> :last-child': {
|
'> :nth-child(2)': {
|
||||||
flex: props.grow === 2 ? growStyle : fixedStyle,
|
flex: props.grow === 2 ? growStyle : fixedStyle,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, {CSSProperties} from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import FlexColumn from './FlexColumn';
|
import FlexColumn from './FlexColumn';
|
||||||
import FlexBox from './FlexBox';
|
import FlexBox from './FlexBox';
|
||||||
@@ -70,6 +70,7 @@ export default class Panel extends React.Component<
|
|||||||
* padding is applied to the heading.
|
* padding is applied to the heading.
|
||||||
*/
|
*/
|
||||||
accessory?: React.ReactNode;
|
accessory?: React.ReactNode;
|
||||||
|
style?: CSSProperties;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
@@ -88,8 +89,10 @@ export default class Panel extends React.Component<
|
|||||||
static PanelContainer = styled(FlexColumn)<{
|
static PanelContainer = styled(FlexColumn)<{
|
||||||
floating?: boolean;
|
floating?: boolean;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
|
grow?: boolean;
|
||||||
}>((props) => ({
|
}>((props) => ({
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
flexGrow: props.grow ? 1 : undefined,
|
||||||
padding: props.floating ? 10 : 0,
|
padding: props.floating ? 10 : 0,
|
||||||
borderBottom: props.collapsed ? 'none' : BORDER,
|
borderBottom: props.collapsed ? 'none' : BORDER,
|
||||||
}));
|
}));
|
||||||
@@ -141,6 +144,7 @@ export default class Panel extends React.Component<
|
|||||||
heading,
|
heading,
|
||||||
collapsable,
|
collapsable,
|
||||||
accessory,
|
accessory,
|
||||||
|
style,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {collapsed} = this.state;
|
const {collapsed} = this.state;
|
||||||
return (
|
return (
|
||||||
@@ -148,7 +152,8 @@ export default class Panel extends React.Component<
|
|||||||
className={className}
|
className={className}
|
||||||
floating={floating}
|
floating={floating}
|
||||||
grow={grow}
|
grow={grow}
|
||||||
collapsed={collapsed}>
|
collapsed={collapsed}
|
||||||
|
style={style}>
|
||||||
<Panel.PanelHeader
|
<Panel.PanelHeader
|
||||||
floating={floating}
|
floating={floating}
|
||||||
padded={padded || typeof heading === 'string'}
|
padded={padded || typeof heading === 'string'}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Component} from 'react';
|
import {Component, CSSProperties} from 'react';
|
||||||
import Text from './Text';
|
import Text from './Text';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -54,6 +54,7 @@ export default class Select extends Component<{
|
|||||||
|
|
||||||
/** Whether the user can interact with the select and change the selcted option */
|
/** Whether the user can interact with the select and change the selcted option */
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
style?: CSSProperties;
|
||||||
}> {
|
}> {
|
||||||
selectID: string = Math.random().toString(36);
|
selectID: string = Math.random().toString(36);
|
||||||
|
|
||||||
@@ -67,7 +68,15 @@ export default class Select extends Component<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {className, options, selected, label, grow, disabled} = this.props;
|
const {
|
||||||
|
className,
|
||||||
|
options,
|
||||||
|
selected,
|
||||||
|
label,
|
||||||
|
grow,
|
||||||
|
disabled,
|
||||||
|
style,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
let select = (
|
let select = (
|
||||||
<SelectMenu
|
<SelectMenu
|
||||||
@@ -76,7 +85,8 @@ export default class Select extends Component<{
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
className={className}
|
className={className}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={selected || ''}>
|
value={selected || ''}
|
||||||
|
style={style}>
|
||||||
{Object.keys(options).map((key, index) => (
|
{Object.keys(options).map((key, index) => (
|
||||||
<option value={key} key={index}>
|
<option value={key} key={index}>
|
||||||
{options[key]}
|
{options[key]}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ const TabContent = styled.div({
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
});
|
});
|
||||||
TabContent.displayName = 'Tabs:TabContent';
|
TabContent.displayName = 'Tabs:TabContent';
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import {colors} from './colors';
|
|||||||
import FlexRow from './FlexRow';
|
import FlexRow from './FlexRow';
|
||||||
import FlexBox from './FlexBox';
|
import FlexBox from './FlexBox';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import {Space} from 'antd';
|
|
||||||
import {useIsSandy} from '../../sandy-chrome/SandyContext';
|
import {useIsSandy} from '../../sandy-chrome/SandyContext';
|
||||||
import {theme} from '../../sandy-chrome/theme';
|
import {theme} from '../../sandy-chrome/theme';
|
||||||
|
import Layout from './Layout';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A toolbar.
|
* A toolbar.
|
||||||
@@ -42,8 +42,8 @@ const ToolbarContainer = styled(FlexRow)<{
|
|||||||
}));
|
}));
|
||||||
ToolbarContainer.displayName = 'ToolbarContainer';
|
ToolbarContainer.displayName = 'ToolbarContainer';
|
||||||
|
|
||||||
const SandyToolbarContainer = styled(Space)({
|
const SandyToolbarContainer = styled(Layout.Horizontal)({
|
||||||
width: '100%',
|
flexWrap: 'wrap',
|
||||||
padding: theme.space.small,
|
padding: theme.space.small,
|
||||||
boxShadow: `inset 0px -1px 0px ${theme.dividerColor}`,
|
boxShadow: `inset 0px -1px 0px ${theme.dividerColor}`,
|
||||||
});
|
});
|
||||||
@@ -65,7 +65,9 @@ export default function Toolbar({
|
|||||||
}) {
|
}) {
|
||||||
const isSandy = useIsSandy();
|
const isSandy = useIsSandy();
|
||||||
return isSandy ? (
|
return isSandy ? (
|
||||||
<SandyToolbarContainer style={style}>{children}</SandyToolbarContainer>
|
<SandyToolbarContainer style={style} gap={theme.space.small} center>
|
||||||
|
{children}
|
||||||
|
</SandyToolbarContainer>
|
||||||
) : (
|
) : (
|
||||||
<ToolbarContainer style={style} {...rest}>
|
<ToolbarContainer style={style} {...rest}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {Filter} from '../filter/types';
|
|||||||
import {TableColumns} from '../table/types';
|
import {TableColumns} from '../table/types';
|
||||||
import {PureComponent} from 'react';
|
import {PureComponent} from 'react';
|
||||||
import Toolbar from '../Toolbar';
|
import Toolbar from '../Toolbar';
|
||||||
import FlexRow from '../FlexRow';
|
|
||||||
import Input from '../Input';
|
import Input from '../Input';
|
||||||
import {colors} from '../colors';
|
import {colors} from '../colors';
|
||||||
import Text from '../Text';
|
import Text from '../Text';
|
||||||
@@ -23,6 +22,7 @@ import {debounce} from 'lodash';
|
|||||||
import ToggleButton from '../ToggleSwitch';
|
import ToggleButton from '../ToggleSwitch';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from '../Layout';
|
import Layout from '../Layout';
|
||||||
|
import {theme} from '../../../sandy-chrome/theme';
|
||||||
|
|
||||||
const SearchBar = styled(Toolbar)({
|
const SearchBar = styled(Toolbar)({
|
||||||
height: 42,
|
height: 42,
|
||||||
@@ -33,13 +33,14 @@ SearchBar.displayName = 'Searchable:SearchBar';
|
|||||||
export const SearchBox = styled(FlexBox)<{isInvalidInput?: boolean}>(
|
export const SearchBox = styled(FlexBox)<{isInvalidInput?: boolean}>(
|
||||||
(props) => {
|
(props) => {
|
||||||
return {
|
return {
|
||||||
|
flex: `1 0 0`,
|
||||||
|
minWidth: 150,
|
||||||
|
height: 30,
|
||||||
backgroundColor: colors.white,
|
backgroundColor: colors.white,
|
||||||
borderRadius: '999em',
|
borderRadius: '999em',
|
||||||
border: `1px solid ${
|
border: `1px solid ${
|
||||||
!props.isInvalidInput ? colors.light15 : colors.red
|
!props.isInvalidInput ? colors.light15 : colors.red
|
||||||
}`,
|
}`,
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
paddingLeft: 4,
|
paddingLeft: 4,
|
||||||
};
|
};
|
||||||
@@ -60,6 +61,7 @@ export const SearchInput = styled(Input)<{
|
|||||||
height: 'auto',
|
height: 'auto',
|
||||||
lineHeight: '100%',
|
lineHeight: '100%',
|
||||||
marginLeft: 2,
|
marginLeft: 2,
|
||||||
|
marginRight: 8,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
color: props.regex && !props.isValidInput ? colors.red : colors.black,
|
color: props.regex && !props.isValidInput ? colors.red : colors.black,
|
||||||
'&::-webkit-input-placeholder': {
|
'&::-webkit-input-placeholder': {
|
||||||
@@ -97,9 +99,8 @@ export const SearchIcon = styled(Glyph)({
|
|||||||
});
|
});
|
||||||
SearchIcon.displayName = 'Searchable:SearchIcon';
|
SearchIcon.displayName = 'Searchable:SearchIcon';
|
||||||
|
|
||||||
const Actions = styled(FlexRow)({
|
const Actions = styled(Layout.Horizontal)({
|
||||||
marginLeft: 8,
|
marginLeft: 8,
|
||||||
flexShrink: 0,
|
|
||||||
});
|
});
|
||||||
Actions.displayName = 'Searchable:Actions';
|
Actions.displayName = 'Searchable:Actions';
|
||||||
|
|
||||||
@@ -533,7 +534,9 @@ export default function Searchable(
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{actions != null && <Actions>{actions}</Actions>}
|
{actions != null && (
|
||||||
|
<Actions gap={theme.space.small}>{actions}</Actions>
|
||||||
|
)}
|
||||||
</SearchBar>
|
</SearchBar>
|
||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@@ -1283,12 +1283,14 @@ export default class DatabasesPlugin extends FlipperPlugin<
|
|||||||
this.state.databases[this.state.selectedDatabase - 1]?.name
|
this.state.databases[this.state.selectedDatabase - 1]?.name
|
||||||
}
|
}
|
||||||
onChange={this.onDatabaseSelected}
|
onChange={this.onDatabaseSelected}
|
||||||
|
style={{maxWidth: 300}}
|
||||||
/>
|
/>
|
||||||
<BoldSpan style={{marginLeft: 16, marginRight: 16}}>Table</BoldSpan>
|
<BoldSpan style={{marginLeft: 16, marginRight: 16}}>Table</BoldSpan>
|
||||||
<Select
|
<Select
|
||||||
options={tableOptions}
|
options={tableOptions}
|
||||||
selected={this.state.selectedDatabaseTable}
|
selected={this.state.selectedDatabaseTable}
|
||||||
onChange={this.onDatabaseTableSelected}
|
onChange={this.onDatabaseTableSelected}
|
||||||
|
style={{maxWidth: 300}}
|
||||||
/>
|
/>
|
||||||
<div />
|
<div />
|
||||||
<Button onClick={this.onRefreshClicked}>Refresh</Button>
|
<Button onClick={this.onRefreshClicked}>Refresh</Button>
|
||||||
|
|||||||
@@ -531,9 +531,7 @@ export function Component() {
|
|||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
isMockResponseSupported={isMockResponseSupported}
|
isMockResponseSupported={isMockResponseSupported}
|
||||||
/>
|
/>
|
||||||
<DetailSidebar width={500}>
|
<Sidebar />
|
||||||
<Sidebar />
|
|
||||||
</DetailSidebar>
|
|
||||||
</NetworkRouteContext.Provider>
|
</NetworkRouteContext.Provider>
|
||||||
</FlexColumn>
|
</FlexColumn>
|
||||||
);
|
);
|
||||||
@@ -759,13 +757,15 @@ function Sidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequestDetails
|
<DetailSidebar width={500}>
|
||||||
key={selectedId}
|
<RequestDetails
|
||||||
request={requestWithId}
|
key={selectedId}
|
||||||
response={responses[selectedId]}
|
request={requestWithId}
|
||||||
bodyFormat={detailBodyFormat}
|
response={responses[selectedId]}
|
||||||
onSelectFormat={instance.onSelectFormat}
|
bodyFormat={detailBodyFormat}
|
||||||
/>
|
onSelectFormat={instance.onSelectFormat}
|
||||||
|
/>
|
||||||
|
</DetailSidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
"caution-triangle": [
|
"caution-triangle": [
|
||||||
12,
|
12,
|
||||||
16,
|
16,
|
||||||
|
20,
|
||||||
24
|
24
|
||||||
],
|
],
|
||||||
"caution": [
|
"caution": [
|
||||||
@@ -512,5 +513,11 @@
|
|||||||
],
|
],
|
||||||
"app-flash": [
|
"app-flash": [
|
||||||
16
|
16
|
||||||
|
],
|
||||||
|
"sample-lo": [
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"point": [
|
||||||
|
20
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user