From 95638af32103d8f5656d9cce7516fb30a22f4dae Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Mon, 21 Sep 2020 11:50:45 -0700 Subject: [PATCH] Build main layout Summary: This diff introduces the. main sections and restyled resizable panes according to the Figma design Reviewed By: cekkaewnumchai Differential Revision: D23758349 fbshipit-source-id: 7f09574f6b5fb54551141c13667c664e1769f09a --- desktop/app/src/sandy-chrome/LeftRail.tsx | 7 +- desktop/app/src/sandy-chrome/SandyApp.tsx | 171 ++++++------------ .../src/sandy-chrome/TemporarilyTitlebar.tsx | 65 +++++++ desktop/app/src/sandy-chrome/theme.tsx | 6 + desktop/app/src/ui/components/Interactive.tsx | 10 +- desktop/app/src/ui/components/Layout.tsx | 40 +++- desktop/app/src/ui/components/Sidebar.tsx | 80 +++++++- .../ui/components/searchable/Searchable.tsx | 17 +- desktop/app/src/ui/index.tsx | 1 + desktop/static/{empy.css => empty.css} | 0 desktop/themes/base.less | 2 +- 11 files changed, 257 insertions(+), 142 deletions(-) create mode 100644 desktop/app/src/sandy-chrome/TemporarilyTitlebar.tsx rename desktop/static/{empy.css => empty.css} (100%) diff --git a/desktop/app/src/sandy-chrome/LeftRail.tsx b/desktop/app/src/sandy-chrome/LeftRail.tsx index cdc8b819f..45a74d3b9 100644 --- a/desktop/app/src/sandy-chrome/LeftRail.tsx +++ b/desktop/app/src/sandy-chrome/LeftRail.tsx @@ -36,12 +36,13 @@ const LeftRailSection = styled(FlexColumn)({ }); LeftRailSection.displayName = 'LeftRailSection'; -const LeftRailButtonElem = styled(Button)<{small?: boolean}>(({small}) => ({ +const LeftRailButtonElem = styled(Button)<{margin: number}>(({margin}) => ({ width: 36, height: 36, - margin: small ? 2 : 6, + margin, padding: '5px 0', border: 'none', + boxShadow: 'none', })); LeftRailButtonElem.displayName = 'LeftRailButtonElem'; @@ -66,7 +67,7 @@ function LeftRailButton({ return ( diff --git a/desktop/app/src/sandy-chrome/SandyApp.tsx b/desktop/app/src/sandy-chrome/SandyApp.tsx index f7f5b94f0..b73f34f0f 100644 --- a/desktop/app/src/sandy-chrome/SandyApp.tsx +++ b/desktop/app/src/sandy-chrome/SandyApp.tsx @@ -8,135 +8,84 @@ */ import React from 'react'; -import {connect} from 'react-redux'; -import {State as Store} from '../reducers'; -import {Settings, updateSettings} from '../reducers/settings'; -import {styled, FlexColumn, colors, Text} from 'flipper'; -import {DatePicker, Button} from 'antd'; -import {Layout, FlexBox} from '../ui'; +import {styled} from 'flipper'; +import {DatePicker} from 'antd'; +import {Layout, FlexRow} from '../ui'; import {theme} from './theme'; import {LeftRail} from './LeftRail'; -import {CloseCircleOutlined} from '@ant-design/icons'; +import {TemporarilyTitlebar} from './TemporarilyTitlebar'; -type StateFromProps = {settings: Settings}; -type DispatchFromProps = {disableSandy: (settings: Settings) => void}; -type OwnProps = {}; - -type Props = StateFromProps & DispatchFromProps & OwnProps; - -const Container = styled(FlexColumn)({ - height: '100%', - width: '100%', - justifyContent: 'center', - alignItems: 'center', - backgroundColor: colors.light02, -}); - -const Box = styled(FlexColumn)({ - justifyContent: 'center', - alignItems: 'center', - background: colors.white, - borderRadius: 10, - boxShadow: '0 1px 3px rgba(0,0,0,0.25)', - paddingBottom: 16, -}); - -const AnnoucementText = styled(Text)({ - fontSize: 24, - fontWeight: 300, - textAlign: 'center', - margin: 16, - color: theme.primaryColor, - background: theme.backgroundWash, -}); - -const LeftContainer = styled(FlexBox)({ - height: '100% ', -}); - -// This component should be dropped, and insetTitlebar should be removed from Electron startup once Sandy is the default -const TemporarilyTitlebar = styled('div')<{focused?: boolean}>(({focused}) => ({ - textAlign: 'center', - userSelect: 'none', - height: '38px', - lineHeight: '38px', - fontSize: '10pt', - color: colors.macOSTitleBarIcon, - background: true - ? `linear-gradient(to bottom, ${colors.macOSTitleBarBackgroundTop} 0%, ${colors.macOSTitleBarBackgroundBottom} 100%)` - : colors.macOSTitleBarBackgroundBlur, - borderBottom: `1px solid ${ - focused ? colors.macOSTitleBarBorder : colors.macOSTitleBarBorderBlur - }`, - WebkitAppRegion: 'drag', -})); - -function SandyApp(props: Props) { +export function SandyApp() { return ( - - [Sandy] Flipper{' '} - - - - + + + - - - - - - - - +
LeftMenu
+ + + + + + + + + + + + + + +
); } -function LeftMenu() { - return
LeftMenu
; -} +const LeftMenu = styled(FlexRow)({ + boxShadow: `inset -1px 0px 0px ${theme.dividerColor}`, + height: '100%', + width: '100%', +}); + +const MainContainer = styled('div')({ + display: 'flex', + width: '100%', + height: '100%', + background: theme.backgroundWash, + paddingRight: theme.space.middle, +}); + +export const ContentContainer = styled('div')({ + width: '100%', + margin: 0, + padding: 0, + background: theme.backgroundDefault, + border: `1px solid ${theme.dividerColor}`, + borderRadius: theme.space.small, + boxShadow: `0px 0px 5px rgba(0, 0, 0, 0.05), 0px 0px 1px rgba(0, 0, 0, 0.05)`, +}); + +const MainContentWrapper = styled('div')({ + height: '100%', + width: '100%', + display: 'flex', + alignItems: 'stretch', + padding: `${theme.space.middle}px 0`, +}); function RightMenu() { return
RightMenu
; } -function MainContainer({children}: any) { - return ( -
- MainContainer -
- {children} -
- ); -} - function TemporarilyContent() { return ( - - - - New UI for Flipper, Sandy Project! Nothing to see now. Go back to - current Flipper - - - - + <> + New UI for Flipper, Sandy Project! Nothing to see now. Go back to current + Flipper + + ); } - -export default connect( - ({settingsState}) => ({settings: settingsState}), - (dispatch) => ({ - disableSandy: (settings: Settings) => { - console.log(settings); - dispatch(updateSettings({...settings, enableSandy: false})); - }, - }), -)(SandyApp); diff --git a/desktop/app/src/sandy-chrome/TemporarilyTitlebar.tsx b/desktop/app/src/sandy-chrome/TemporarilyTitlebar.tsx new file mode 100644 index 000000000..dbb2a015a --- /dev/null +++ b/desktop/app/src/sandy-chrome/TemporarilyTitlebar.tsx @@ -0,0 +1,65 @@ +/** + * 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 {connect} from 'react-redux'; +import {State as Store} from '../reducers'; +import {Settings, updateSettings} from '../reducers/settings'; +import {styled, colors} from 'flipper'; +import {Button} from 'antd'; +import {CloseCircleOutlined} from '@ant-design/icons'; + +type StateFromProps = {settings: Settings}; +type DispatchFromProps = {disableSandy: (settings: Settings) => void}; +type OwnProps = {}; + +type Props = StateFromProps & DispatchFromProps & OwnProps; + +// This component should be dropped, and insetTitlebar should be removed from Electron startup once Sandy is the default +const TemporarilyTitlebarContainer = styled('div')<{focused?: boolean}>( + ({focused}) => ({ + textAlign: 'center', + userSelect: 'none', + height: '38px', + lineHeight: '38px', + fontSize: '10pt', + color: colors.macOSTitleBarIcon, + background: true + ? `linear-gradient(to bottom, ${colors.macOSTitleBarBackgroundTop} 0%, ${colors.macOSTitleBarBackgroundBottom} 100%)` + : colors.macOSTitleBarBackgroundBlur, + borderBottom: `1px solid ${ + focused ? colors.macOSTitleBarBorder : colors.macOSTitleBarBorderBlur + }`, + WebkitAppRegion: 'drag', + }), +); + +export const TemporarilyTitlebar = connect< + StateFromProps, + DispatchFromProps, + OwnProps, + Store +>( + ({settingsState}) => ({settings: settingsState}), + (dispatch) => ({ + disableSandy: (settings: Settings) => { + console.log(settings); + dispatch(updateSettings({...settings, enableSandy: false})); + }, + }), +)((props: Props) => ( + + [Sandy] Flipper{' '} + + +)); diff --git a/desktop/app/src/sandy-chrome/theme.tsx b/desktop/app/src/sandy-chrome/theme.tsx index 1b30492f0..9e5b7b26a 100644 --- a/desktop/app/src/sandy-chrome/theme.tsx +++ b/desktop/app/src/sandy-chrome/theme.tsx @@ -22,4 +22,10 @@ export const theme = { backgroundWash: 'var(--flipper-background-wash)', dividerColor: 'var(--flipper-divider-color)', borderRadius: 'var(--flipper-border-radius)', + space: { + // from Space component in Ant + small: 8, + middle: 16, + large: 24, + } as const, }; diff --git a/desktop/app/src/ui/components/Interactive.tsx b/desktop/app/src/ui/components/Interactive.tsx index 8de4d6eee..ac64251b9 100644 --- a/desktop/app/src/ui/components/Interactive.tsx +++ b/desktop/app/src/ui/components/Interactive.tsx @@ -76,6 +76,7 @@ type InteractiveProps = { style?: Object; className?: string; children?: React.ReactNode; + gutterWidth?: number; }; type InteractiveState = { @@ -549,11 +550,12 @@ export default class Interactive extends React.Component< const x = event.clientX - offsetLeft; const y = event.clientY - offsetTop; - const atTop: boolean = y <= WINDOW_CURSOR_BOUNDARY; - const atBottom: boolean = y >= height - WINDOW_CURSOR_BOUNDARY; + const gutterWidth = this.props.gutterWidth || WINDOW_CURSOR_BOUNDARY; + const atTop: boolean = y <= gutterWidth; + const atBottom: boolean = y >= height - gutterWidth; - const atLeft: boolean = x <= WINDOW_CURSOR_BOUNDARY; - const atRight: boolean = x >= width - WINDOW_CURSOR_BOUNDARY; + const atLeft: boolean = x <= gutterWidth; + const atRight: boolean = x >= width - gutterWidth; return { bottom: canResize.bottom === true && atBottom, diff --git a/desktop/app/src/ui/components/Layout.tsx b/desktop/app/src/ui/components/Layout.tsx index 605fd07a5..9e76803aa 100644 --- a/desktop/app/src/ui/components/Layout.tsx +++ b/desktop/app/src/ui/components/Layout.tsx @@ -9,9 +9,20 @@ import React from 'react'; import styled from '@emotion/styled'; +import {Sidebar} from '..'; type Props = { + /** + * If set, the dynamically sized pane will get scrollbars when needed + */ scrollable?: boolean; + /** + * If set, the 'fixed' child will no longer be sized based on it's own dimensions, + * but rather it will be possible to resize it + */ + initialSize?: number; + minSize?: number; + children: [React.ReactNode, React.ReactNode]; }; @@ -42,16 +53,35 @@ const Container = styled('div')<{horizontal: boolean}>(({horizontal}) => ({ Container.displayName = 'Layout:Container'; function renderLayout( - {children, scrollable}: Props, + {children, scrollable, initialSize, minSize}: Props, horizontal: boolean, reverse: boolean, ) { if (children.length !== 2) { throw new Error('Layout expects exactly 2 children'); } - const fixedElement = ( - {reverse ? children[1] : children[0]} - ); + const fixedChild = reverse ? children[1] : children[0]; + + const fixedElement = + initialSize === undefined ? ( + {fixedChild} + ) : horizontal ? ( + + {fixedChild} + + ) : ( + + {fixedChild} + + ); const dynamicElement = ( {reverse ? children[0] : children[1]} @@ -77,6 +107,8 @@ function renderLayout( * 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. */ const Layout: Record<'Left' | 'Right' | 'Top' | 'Bottom', React.FC> = { diff --git a/desktop/app/src/ui/components/Sidebar.tsx b/desktop/app/src/ui/components/Sidebar.tsx index 2c0422955..fbe20b624 100644 --- a/desktop/app/src/ui/components/Sidebar.tsx +++ b/desktop/app/src/ui/components/Sidebar.tsx @@ -10,7 +10,7 @@ import Interactive from './Interactive'; import FlexColumn from './FlexColumn'; import {colors} from './colors'; -import {Component} from 'react'; +import {Component, ReactNode} from 'react'; import styled from '@emotion/styled'; import { BackgroundClipProperty, @@ -19,6 +19,9 @@ import { BackgroundColorProperty, } from 'csstype'; import React from 'react'; +import FlexRow from './FlexRow'; +import {MoreOutlined} from '@ant-design/icons'; +import {theme} from '../../sandy-chrome/theme'; const SidebarInteractiveContainer = styled(Interactive)({ flex: 'none', @@ -31,12 +34,18 @@ const SidebarContainer = styled(FlexColumn)<{ position: 'right' | 'top' | 'left' | 'bottom'; backgroundColor?: BackgroundClipProperty; overflow?: boolean; + unstyled?: boolean; }>((props) => ({ - backgroundColor: props.backgroundColor || colors.macOSTitleBarBackgroundBlur, - borderLeft: props.position === 'right' ? '1px solid #b3b3b3' : 'none', - borderTop: props.position === 'bottom' ? '1px solid #b3b3b3' : 'none', - borderRight: props.position === 'left' ? '1px solid #b3b3b3' : 'none', - borderBottom: props.position === 'top' ? '1px solid #b3b3b3' : 'none', + ...(props.unstyled + ? undefined + : { + backgroundColor: + props.backgroundColor || colors.macOSTitleBarBackgroundBlur, + borderLeft: props.position === 'right' ? '1px solid #b3b3b3' : 'none', + borderTop: props.position === 'bottom' ? '1px solid #b3b3b3' : 'none', + borderRight: props.position === 'left' ? '1px solid #b3b3b3' : 'none', + borderBottom: props.position === 'top' ? '1px solid #b3b3b3' : 'none', + }), height: '100%', overflowX: 'hidden', overflowY: 'auto', @@ -93,6 +102,10 @@ type SidebarProps = { * Class name to customise styling. */ className?: string; + /** + * use a Sandy themed large gutter + */ + gutter?: boolean; }; type SidebarState = { @@ -138,7 +151,7 @@ export default class Sidebar extends Component { }; render() { - const {backgroundColor, onResize, position, children} = this.props; + const {backgroundColor, onResize, position, children, gutter} = this.props; let height: number | undefined; let minHeight: number | undefined; let maxHeight: number | undefined; @@ -183,11 +196,58 @@ export default class Sidebar extends Component { maxHeight={maxHeight} height={!horizontal ? (onResize ? height : this.state.height) : '100%'} resizable={resizable} - onResize={this.onResize}> - - {children} + onResize={this.onResize} + gutterWidth={gutter ? theme.space.middle : undefined}> + + {gutter ? ( + {children} + ) : ( + children + )} ); } } + +const GutterWrapper = ({ + position, + children, +}: { + position: SidebarPosition; + children: ReactNode; +}) => { + return position === 'right' ? ( + + + {children} + + ) : ( + + {children} + + + ); // TODO: support top / bottom +}; + +const VerticalGutterContainer = styled('div')({ + width: theme.space.middle, + height: '100%', + color: theme.textColorPlaceholder, + fontSize: '16px', + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + background: theme.backgroundWash, + ':hover': { + background: theme.dividerColor, + }, +}); +const VerticalGutter = () => ( + + + +); diff --git a/desktop/app/src/ui/components/searchable/Searchable.tsx b/desktop/app/src/ui/components/searchable/Searchable.tsx index 868cb2184..d5a9128eb 100644 --- a/desktop/app/src/ui/components/searchable/Searchable.tsx +++ b/desktop/app/src/ui/components/searchable/Searchable.tsx @@ -144,10 +144,14 @@ function compileRegex(s: string): RegExp | null { } } -const Searchable = ( +/** + * Higher-order-component that allows adding a searchbar on top of the wrapped + * component. See SearchableManagedTable for usage with a table. + */ +export default function Searchable( Component: React.ComponentType, -): React.ComponentType => - class extends PureComponent { +): React.ComponentType { + return class extends PureComponent { static displayName = `Searchable(${Component.displayName})`; static defaultProps = { @@ -544,9 +548,4 @@ const Searchable = ( ); } }; - -/** - * Higher-order-component that allows adding a searchbar on top of the wrapped - * component. See SearchableManagedTable for usage with a table. - */ -export default Searchable; +} diff --git a/desktop/app/src/ui/index.tsx b/desktop/app/src/ui/index.tsx index b387d8156..0b364543f 100644 --- a/desktop/app/src/ui/index.tsx +++ b/desktop/app/src/ui/index.tsx @@ -176,5 +176,6 @@ 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'; diff --git a/desktop/static/empy.css b/desktop/static/empty.css similarity index 100% rename from desktop/static/empy.css rename to desktop/static/empty.css diff --git a/desktop/themes/base.less b/desktop/themes/base.less index 83e2e0e61..ae725f6ef 100644 --- a/desktop/themes/base.less +++ b/desktop/themes/base.less @@ -23,7 +23,7 @@ /** This section maps theme colors to CSS variables so that thye can be - used in styled components, see sandyColors.tsx + used in styled components, see theme.tsx */ :root {