Bless "Layout" and "theme"

Summary: This blesses the `Layout` and `theme` components and exposes them from `flipper-plugin`, so that they can be used in (public) Sandy plugins. Also marked old abstractions as going to be deprecated.

Reviewed By: cekkaewnumchai

Differential Revision: D24503560

fbshipit-source-id: a8f384667b8f66e3b9f00771a123fe5c9d755eb3
This commit is contained in:
Michel Weststrate
2020-10-27 05:16:10 -07:00
committed by Facebook GitHub Bot
parent dfdc02fbc2
commit 9f3df3406d
33 changed files with 59 additions and 120 deletions

View File

@@ -15,8 +15,9 @@ import type {Methods} from 'console-feed/lib/definitions/Methods';
import type {Styles} from 'console-feed/lib/definitions/Styles';
import {createState, useValue} from 'flipper-plugin';
import {useLocalStorage} from '../utils/useLocalStorage';
import {theme, useIsDarkMode} from '../sandy-chrome/theme';
import {theme} from 'flipper-plugin';
import {useIsSandy} from '../sandy-chrome/SandyContext';
import {useIsDarkMode} from '../utils/useIsDarkMode';
const MAX_LOG_ITEMS = 1000;

View File

@@ -9,7 +9,7 @@
import {FlexColumn, styled, FlexRow, ToggleButton} from '../../ui';
import React from 'react';
import {theme} from '../../sandy-chrome/theme';
import {theme} from 'flipper-plugin';
const IndentedSection = styled(FlexColumn)({
paddingLeft: 50,

View File

@@ -8,7 +8,7 @@
*/
import {Layout, styled} from '../ui';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
export const ContentContainer = styled(Layout.Container)({
flex: 1,

View File

@@ -10,7 +10,7 @@
import React from 'react';
import {Typography, Card, Table, Collapse, Button, Tabs} from 'antd';
import {Layout} from '../ui';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
import reactElementToJSXString from 'react-element-to-jsx-string';
import {CodeOutlined} from '@ant-design/icons';

View File

@@ -26,7 +26,7 @@ import {
toggleLeftSidebarVisible,
toggleRightSidebarVisible,
} from '../reducers/application';
import {theme} from './theme';
import {theme, Layout} from 'flipper-plugin';
import SetupDoctorScreen, {checkHasNewProblem} from './SetupDoctorScreen';
import SettingsSheet from '../chrome/SettingsSheet';
import WelcomeScreen from './WelcomeScreen';
@@ -36,7 +36,6 @@ import {ToplevelProps} from './SandyApp';
import {useValue} from 'flipper-plugin';
import {logout} from '../reducers/user';
import config from '../fb-stubs/config';
import {Layout} from '../ui/components/Layout';
import styled from '@emotion/styled';
const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({

View File

@@ -8,7 +8,7 @@
*/
import React from 'react';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
import styled from '@emotion/styled';
import {Layout} from '../ui';
import {Button, Tooltip, Typography} from 'antd';

View File

@@ -10,7 +10,7 @@
import React, {useEffect, useState, useCallback} from 'react';
import {styled} from '../ui';
import {Layout, Sidebar} from '../ui';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
import {Logger} from '../fb-interfaces/Logger';
import {LeftRail} from './LeftRail';

View File

@@ -10,7 +10,7 @@
import React from 'react';
import {Typography, Button, Space, Input, Card, Alert, List} from 'antd';
import {Layout} from '../ui';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
import {css} from 'emotion';
import {DesignComponentDemos} from './DesignComponentDemos';

View File

@@ -26,7 +26,7 @@ import {
HealthcheckStatus,
HealthcheckResult,
} from '../reducers/healthchecks';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
import {
startHealthchecks,
updateHealthcheckResult,

View File

@@ -16,7 +16,7 @@ import {
CodeOutlined,
BugOutlined,
} from '@ant-design/icons';
import {theme} from './theme';
import {theme} from 'flipper-plugin';
const {Text, Title} = Typography;

View File

@@ -12,7 +12,7 @@ import {Alert, Button, Input} from 'antd';
import {LeftSidebar, SidebarTitle, InfoIcon} from '../LeftSidebar';
import {SettingOutlined, RocketOutlined} from '@ant-design/icons';
import {Layout, Link} from '../../ui';
import {theme} from '../theme';
import {theme} from 'flipper-plugin';
import {useStore as useReduxStore} from 'react-redux';
import {showEmulatorLauncher} from './LaunchEmulator';
import {AppSelector} from './AppSelector';

View File

@@ -16,7 +16,7 @@ import {
CaretDownOutlined,
} from '@ant-design/icons';
import {Layout, styled} from '../../ui';
import {theme} from '../theme';
import {theme} from 'flipper-plugin';
import {batch} from 'react-redux';
import {Dispatch, useDispatch, useStore} from '../../utils/useStore';
import {

View File

@@ -14,7 +14,7 @@ import {renderReactRoot} from '../../utils/renderReactRoot';
import {Store} from '../../reducers';
import {useStore} from '../../utils/useStore';
import {launchEmulator} from '../../devices/AndroidDevice';
import {Layout} from '../../ui/components/Layout';
import {Layout} from 'flipper-plugin';
import {
launchSimulator,
getSimulators,

View File

@@ -12,7 +12,7 @@ import {Badge, Button, Menu, Tooltip, Typography} from 'antd';
import {InfoIcon, SidebarTitle} from '../LeftSidebar';
import {PlusOutlined, MinusOutlined} from '@ant-design/icons';
import {Glyph, Layout, styled} from '../../ui';
import {theme} from '../theme';
import {theme} from 'flipper-plugin';
import {useDispatch, useStore} from '../../utils/useStore';
import {getPluginTitle, sortPluginsByName} from '../../utils/pluginUtils';
import {ClientPluginDefinition, DevicePluginDefinition} from '../../plugin';

View File

@@ -8,7 +8,8 @@
*/
import React from 'react';
import {Layout, styled} from '../../ui';
import {Layout, theme} from 'flipper-plugin';
import {styled} from '../../ui';
import {Input, Typography, Button, Collapse} from 'antd';
import {
DownOutlined,
@@ -20,7 +21,6 @@ import {
} from '@ant-design/icons';
import {LeftSidebar, SidebarTitle} from '../LeftSidebar';
import {PluginNotification} from '../../reducers/notifications';
import {theme} from '../theme';
const {Title, Text, Paragraph} = Typography;

View File

@@ -1,87 +0,0 @@
/**
* 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 {useStore} from '../utils/useStore';
// Exposes all the variables defined in themes/base.less:
export const theme = {
white: 'white', // use as counter color for primary
primaryColor: 'var(--flipper-primary-color)',
successColor: 'var(--flipper-success-color)',
errorColor: 'var(--flipper-error-color)',
warningColor: 'var(--flipper-warning-color)',
textColorPrimary: 'var(--flipper-text-color-primary)',
textColorSecondary: 'var(--flipper-text-color-secondary)',
textColorPlaceholder: 'var(--flipper-text-color-placeholder)',
disabledColor: 'var(--flipper-disabled-color)',
backgroundDefault: 'var(--flipper-background-default)',
backgroundWash: 'var(--flipper-background-wash)',
buttonDefaultBackground: 'var(--flipper-button-default-background)',
backgroundTransparentHover: 'var(--flipper-background-transparent-hover)',
dividerColor: 'var(--flipper-divider-color)',
borderRadius: 'var(--flipper-border-radius)',
containerBorderRadius: 8,
inlinePaddingV: 6, // vertical padding on inline elements like buttons
inlinePaddingH: 12, // horizontal ,,,
space: {
// from Space component in Ant
tiny: 4,
small: 8,
medium: 12,
large: 16,
huge: 24,
} as const,
fontSize: {
smallBody: '12px',
} as const,
} as const;
/**
* This hook returns whether dark mode is currently being used.
* Generally should be avoided in favor of using the above theme object,
* which will provide colors that reflect the theme
*/
export function useIsDarkMode(): boolean {
return useStore(
(state) => state.settingsState.enableSandy && state.settingsState.darkMode,
);
}
export type Spacing = keyof typeof theme['space'] | number | undefined | true;
export type PaddingProps = {
padv?: Spacing;
padh?: Spacing;
pad?: Spacing;
};
export function normalizePadding({
padv,
padh,
pad,
}: PaddingProps): string | undefined {
if (padv === undefined && padh === undefined && pad === undefined) {
return undefined;
}
return `${normalizeSpace(
padv ?? pad ?? 0,
theme.inlinePaddingV,
)}px ${normalizeSpace(padh ?? pad ?? 0, theme.inlinePaddingH)}px`;
}
export function normalizeSpace(spacing: Spacing, defaultSpace: number): number {
return spacing === true
? defaultSpace
: spacing === undefined
? 0
: typeof spacing === 'string'
? theme.space[spacing]
: spacing;
}

View File

@@ -21,7 +21,7 @@ import {useStore} from '../../utils/useStore';
import {useIsSandy} from '../../sandy-chrome/SandyContext';
import type {ButtonProps} from 'antd/lib/button';
import {DownOutlined, CheckOutlined} from '@ant-design/icons';
import {theme} from '../../sandy-chrome/theme';
import {theme} from 'flipper-plugin';
type ButtonType = 'primary' | 'success' | 'warning' | 'danger';

View File

@@ -16,6 +16,7 @@ type Props = {
};
/**
* @deprecated use `Layout.Container` from flipper-plugin instead
* A container using flexbox to layout its children
*/
const FlexBox = styled(View)<Props>(({shrink}) => ({

View File

@@ -11,6 +11,7 @@ import View from './View';
import styled from '@emotion/styled';
/**
* @deprecated use `Layout.Container` from flipper-plugin instead
* A container displaying its children horizontally and vertically centered.
*/
const FlexCenter = styled(View)({

View File

@@ -11,6 +11,7 @@ import FlexBox from './FlexBox';
import styled from '@emotion/styled';
/**
* @deprecated use `Layout.Container` from flipper-plugin instead
* A container displaying its children in a column
*/
const FlexColumn = styled(FlexBox)({

View File

@@ -11,6 +11,7 @@ import FlexBox from './FlexBox';
import styled from '@emotion/styled';
/**
* @deprecated use `Layout.Horizontal` from flipper-plugin instead
* A container displaying its children in a row
*/
const FlexRow = styled(FlexBox)({

View File

@@ -25,6 +25,9 @@ const HBoxContainer = styled(FlexRow)<{verticalAlign: string}>(
HBoxContainer.displayName = 'HBoxContainer';
/**
* @deprecated use Layout.Left / Layout.Right or Layout.Horizonta from flipper-plugin instead
*/
const HBox: React.FC<{
children: [] | [React.ReactNode] | [React.ReactNode, React.ReactNode];
grow?: 'left' | 'right' | 'auto';

View File

@@ -1,218 +0,0 @@
/**
* 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, {CSSProperties} from 'react';
import styled from '@emotion/styled';
import {
normalizePadding,
normalizeSpace,
PaddingProps,
Spacing,
theme,
} from '../../sandy-chrome/theme';
import {useIsSandy} from '../../sandy-chrome/SandyContext';
import {renderLayout} from './LegacyLayout';
type ContainerProps = {
children?: React.ReactNode;
className?: string;
style?: React.CSSProperties;
borderBottom?: boolean;
borderTop?: boolean;
borderRight?: boolean;
borderLeft?: boolean;
bordered?: boolean;
rounded?: boolean;
width?: number;
height?: number;
// grow to available space?
grow?: boolean;
// allow shrinking beyond minally needed size? Makes using ellipsis on children possible
shrink?: boolean;
/**
* 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;
} & PaddingProps;
const Container = styled.div<ContainerProps>(
({
bordered,
borderBottom,
borderLeft,
borderRight,
borderTop,
rounded,
width,
height,
grow,
shrink,
gap,
center,
...rest
}) => ({
display: 'flex',
flexDirection: 'column',
flex:
grow && shrink
? `1 1 0` // allow growing, and shrinking smaller than actual size
: grow
? `1 0 auto` // allow grow, start at natural size
: shrink
? `0 1 0` // allow shrinking smaller than natural size
: `0 0 auto`, // (default) use natural size, don't grow or shrink (in parent flex direction)
alignItems: center ? 'center' : 'stretch',
gap: normalizeSpace(gap, theme.space.small),
minWidth: shrink ? 0 : undefined,
boxSizing: 'border-box',
width,
height,
padding: normalizePadding(rest),
borderRadius: rounded ? theme.containerBorderRadius : undefined,
borderStyle: 'solid',
borderColor: theme.dividerColor,
borderWidth: bordered
? 1
: `${borderTop ? 1 : 0}px ${borderRight ? 1 : 0}px ${
borderBottom ? 1 : 0
}px ${borderLeft ? 1 : 0}px`,
}),
);
const Horizontal = styled(Container)({
flexDirection: 'row',
});
const ScrollParent = styled.div<{axis?: ScrollAxis}>(({axis}) => ({
flex: 1,
boxSizing: 'border-box',
position: 'relative',
overflowX: axis === 'y' ? 'hidden' : 'auto',
overflowY: axis === 'x' ? 'hidden' : 'auto',
}));
const ScrollChild = styled(Container)<{axis?: ScrollAxis}>(({axis}) => ({
position: 'absolute',
minHeight: '100%',
minWidth: '100%',
maxWidth: axis === 'y' ? '100%' : undefined,
maxHeight: axis === 'x' ? '100%' : undefined,
}));
type ScrollAxis = 'x' | 'y' | 'both';
const ScrollContainer = ({
children,
horizontal,
vertical,
padv,
padh,
pad,
...rest
}: React.HTMLAttributes<HTMLDivElement> & {
horizontal?: boolean;
vertical?: boolean;
} & PaddingProps) => {
const axis =
horizontal && !vertical ? 'x' : !horizontal && vertical ? 'y' : 'both';
return (
<ScrollParent axis={axis} {...rest}>
<ScrollChild axis={axis} padv={padv} padh={padh} pad={pad}>
{children}
</ScrollChild>
</ScrollParent>
) as any;
};
type SplitLayoutProps = {
/**
* If set, items will be centered over the orthogonal direction, if false (the default) items will be stretched.
*/
center?: boolean;
children: [React.ReactNode, React.ReactNode];
};
function renderSplitLayout(
props: SplitLayoutProps,
direction: 'column' | 'row',
grow: 1 | 2,
) {
// eslint-disable-next-line
const isSandy = useIsSandy();
if (!isSandy) return renderLayout(props, direction === 'row', grow === 1);
const [child1, child2] = props.children;
return (
<SandySplitContainer {...props} flexDirection={direction} grow={grow}>
{child1}
{child2}
</SandySplitContainer>
);
}
/**
* The Layout component divides all available screenspace over two components:
* A fixed top (or left) component, and all remaining space to a bottom component.
*
* The main area will be scrollable by default, but if multiple containers are nested,
* scrolling can be disabled by using `scrollable={false}`
*
* If initialSize is set, the fixed container will be made resizable
*
* Use Layout.Top / Right / Bottom / Left to indicate where the fixed element should live.
*/
export const Layout = {
Top(props: SplitLayoutProps) {
return renderSplitLayout(props, 'column', 2);
},
Bottom(props: SplitLayoutProps) {
return renderSplitLayout(props, 'column', 1);
},
Left(props: SplitLayoutProps) {
return renderSplitLayout(props, 'row', 2);
},
Right(props: SplitLayoutProps) {
return renderSplitLayout(props, 'row', 1);
},
Container,
ScrollContainer,
Horizontal,
};
Object.keys(Layout).forEach((key) => {
(Layout as any)[key].displayName = `Layout.${key}`;
});
const SandySplitContainer = styled.div<{
grow: 1 | 2;
center?: boolean;
flexDirection: CSSProperties['flexDirection'];
}>((props) => ({
boxSizing: 'border-box',
display: 'flex',
flex: 1,
flexDirection: props.flexDirection,
alignItems: props.center ? 'center' : 'stretch',
overflow: 'hidden',
'> :nth-child(1)': {
flex: props.grow === 1 ? splitGrowStyle : splitFixedStyle,
minWidth: props.grow === 1 ? 0 : undefined,
},
'> :nth-child(2)': {
flex: props.grow === 2 ? splitGrowStyle : splitFixedStyle,
minWidth: props.grow === 2 ? 0 : undefined,
},
}));
const splitFixedStyle = `0 0 auto`;
const splitGrowStyle = `1 0 0`;

View File

@@ -1,76 +0,0 @@
/**
* 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 FixedContainer = styled.div({
flex: 'none',
height: 'auto',
overflow: 'hidden',
});
FixedContainer.displayName = 'Layout:FixedContainer';
const SplitScrollContainer = styled.div<{scrollable: boolean}>(
({scrollable}) => ({
overflow: scrollable ? 'auto' : 'hidden',
flex: 'auto',
display: 'flex',
}),
);
SplitScrollContainer.displayName = 'Layout:SplitScrollContainer';
const SplitContainer = styled.div<{horizontal: boolean; center?: boolean}>(
({horizontal, center}) => ({
display: 'flex',
flex: 'auto',
flexDirection: horizontal ? 'row' : 'column',
height: '100%',
width: '100%',
overflow: 'hidden',
alignItems: center ? 'center' : undefined,
}),
);
SplitContainer.displayName = 'Layout:SplitContainer';
/**
* @deprecated use Layout.Top|Left|Right|Bottom instead
*/
export function renderLayout(
{
children,
scrollable,
}: {scrollable?: boolean; children: [React.ReactNode, React.ReactNode]},
horizontal: boolean,
reverse: boolean,
) {
if (children.length !== 2) {
throw new Error('Layout expects exactly 2 children');
}
const fixedChild = reverse ? children[1] : children[0];
const fixedElement = <FixedContainer>{fixedChild}</FixedContainer>;
const dynamicElement = (
<SplitScrollContainer scrollable={!!scrollable}>
{reverse ? children[0] : children[1]}
</SplitScrollContainer>
);
return reverse ? (
<SplitContainer horizontal={horizontal}>
{dynamicElement}
{fixedElement}
</SplitContainer>
) : (
<SplitContainer horizontal={horizontal}>
{fixedElement}
{dynamicElement}
</SplitContainer>
);
}

View File

@@ -21,7 +21,7 @@ import {
import React from 'react';
import FlexRow from './FlexRow';
import {MoreOutlined} from '@ant-design/icons';
import {theme} from '../../sandy-chrome/theme';
import {theme} from 'flipper-plugin';
const SidebarInteractiveContainer = styled(Interactive)({
flex: 'none',

View File

@@ -13,8 +13,7 @@ import FlexRow from './FlexRow';
import FlexBox from './FlexBox';
import styled from '@emotion/styled';
import {useIsSandy} from '../../sandy-chrome/SandyContext';
import {theme} from '../../sandy-chrome/theme';
import {Layout} from './Layout';
import {theme, Layout} from 'flipper-plugin';
/**
* A toolbar.

View File

@@ -11,6 +11,7 @@ import styled from '@emotion/styled';
import FlexColumn from './FlexColumn';
/**
* @deprecated use `Layout.Container` from flipper-plugin instead
* Container that applies a standardized bottom margin for vertical spacing
*/
const VBox = styled(FlexColumn)({

View File

@@ -15,6 +15,10 @@ type Props = {
maxHeight?: number;
};
/**
*
* @deprecated use `Layout.Container` from flipper-plugin instead
*/
const View = styled.div<Props>((props) => ({
height: props.grow ? '100%' : 'auto',
overflow: props.scrollable ? 'auto' : 'visible',

View File

@@ -7,7 +7,7 @@
* @format
*/
import {theme} from '../../sandy-chrome/theme';
import {theme} from 'flipper-plugin';
// Last updated: Jan 30 2016

View File

@@ -21,8 +21,7 @@ import styled from '@emotion/styled';
import {debounce} from 'lodash';
import ToggleButton from '../ToggleSwitch';
import React from 'react';
import {Layout} from '../Layout';
import {theme} from '../../../sandy-chrome/theme';
import {Layout, theme} from 'flipper-plugin';
const SearchBar = styled(Toolbar)({
height: 42,

View File

@@ -178,7 +178,7 @@ export {default as CenteredView} from './components/CenteredView';
export {default as Info} from './components/Info';
export {default as Bordered} from './components/Bordered';
export {default as AlternatingRows} from './components/AlternatingRows';
export {Layout} from './components/Layout';
export {Layout} from 'flipper-plugin';
export {default as Scrollable} from './components/Scrollable';
export * from './components/Highlight';

View 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 {useStore} from '../../../app/src/utils/useStore';
/**
* This hook returns whether dark mode is currently being used.
* Generally should be avoided in favor of using the above theme object,
* which will provide colors that reflect the theme
*/
export function useIsDarkMode(): boolean {
return useStore(
(state) => state.settingsState.enableSandy && state.settingsState.darkMode,
);
}