More shaving on design system

Summary: Added standardized dimensions for padding and gap, to encourage people to build layouts that look consistent, using for example `padv="small"`

Reviewed By: cekkaewnumchai

Differential Revision: D23961386

fbshipit-source-id: 33cd3b8974858e021a8b7d1b32f018fe3f007c63
This commit is contained in:
Michel Weststrate
2020-10-01 05:32:07 -07:00
committed by Facebook GitHub Bot
parent e8370e9fc1
commit b105574d00
10 changed files with 148 additions and 107 deletions

View File

@@ -27,6 +27,7 @@ import {notNull} from './utils/typeUtils';
import constants from './fb-stubs/constants'; import constants from './fb-stubs/constants';
import {Logger} from './fb-interfaces/Logger'; import {Logger} from './fb-interfaces/Logger';
import {NormalizedMenuEntry, buildInMenuEntries} from 'flipper-plugin'; import {NormalizedMenuEntry, buildInMenuEntries} from 'flipper-plugin';
import {StyleGuide} from './sandy-chrome/StyleGuide';
export type DefaultKeyboardAction = keyof typeof buildInMenuEntries; export type DefaultKeyboardAction = keyof typeof buildInMenuEntries;
export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help'; export type TopLevelMenu = 'Edit' | 'View' | 'Window' | 'Help';
@@ -339,6 +340,12 @@ function getTemplate(
store.dispatch(setActiveSheet(ACTIVE_SHEET_PLUGINS)); store.dispatch(setActiveSheet(ACTIVE_SHEET_PLUGINS));
}, },
}, },
{
label: 'Flipper style guide',
click() {
store.dispatch(setStaticView(StyleGuide));
},
},
{ {
label: 'Toggle Developer Tools', label: 'Toggle Developer Tools',
accelerator: (function () { accelerator: (function () {

View File

@@ -37,12 +37,10 @@ export function AppInspect() {
<SidebarTitle actions={<InfoIcon>{appTooltip}</InfoIcon>}> <SidebarTitle actions={<InfoIcon>{appTooltip}</InfoIcon>}>
App Inspect App Inspect
</SidebarTitle> </SidebarTitle>
<Layout.Vertical <Layout.Vertical padv="small" padh="medium" gap={theme.space.large}>
padding={`${theme.space.small}px ${theme.space.medium}px`}
gap={theme.space.large}>
<DeviceDropdown /> <DeviceDropdown />
<Input addonAfter={<SettingOutlined />} defaultValue="mysite" /> <Input addonAfter={<SettingOutlined />} defaultValue="mysite" />
<Layout.Horizontal gap={theme.space.small}> <Layout.Horizontal gap>
<Button icon={<SettingOutlined />} type="link" /> <Button icon={<SettingOutlined />} type="link" />
<Button icon={<SettingOutlined />} type="link" /> <Button icon={<SettingOutlined />} type="link" />
<Button icon={<SettingOutlined />} type="link" /> <Button icon={<SettingOutlined />} type="link" />

View File

@@ -72,14 +72,14 @@ const demos: PreviewProps[] = [
props: [ props: [
['rounded', 'boolean (false)', 'Make the corners rounded'], ['rounded', 'boolean (false)', 'Make the corners rounded'],
[ [
'padded', 'padv / padh / pad',
'boolean (false)', Object.keys(theme.space).join(' | ') + ' | number | true',
'Use a standard small padding for this container (use `padding` for non-default padding)', 'Short-hand to set the horizontal, vertical or both paddings. The keys correspond to the theme space settings. Using `true` picks the default horizontal / vertical padding for inline elements.',
], ],
[ [
'padding', 'width / height',
'CSS Padding', 'number',
'Short-hand to set the style.padding property', 'Set the width / height of this container in pixels. Use sparingly.',
], ],
[ [
'bordered', 'bordered',
@@ -87,25 +87,10 @@ const demos: PreviewProps[] = [
'This container will use a default border on all sides', 'This container will use a default border on all sides',
], ],
[ [
'borderTop', 'borderTop / borderRight / borderBottom / borderLeft',
'boolean (false)', 'boolean (false)',
'Use a standard padding on the top side', 'Use a standard padding on the top side',
], ],
[
'borderRight',
'boolean (false)',
'Use a standard padding on the right side',
],
[
'borderBottom',
'boolean (false)',
'Use a standard padding on the bottom side',
],
[
'borderLeft',
'boolean (false)',
'Use a standard padding on the left side',
],
], ],
demos: { demos: {
'Basic container with fixed dimensions': ( 'Basic container with fixed dimensions': (
@@ -149,8 +134,8 @@ const demos: PreviewProps[] = [
props: [ props: [
[ [
'gap', 'gap',
'number (0)', Object.keys(theme.space).join(' | ') + ' | number | true',
'Set the spacing between children. Typically theme.space.small should be used.', 'Set the spacing between children. For `true` theme.space.small should be used. Defaults to 0.',
], ],
[ [
'center', 'center',
@@ -159,8 +144,8 @@ const demos: PreviewProps[] = [
], ],
], ],
demos: { demos: {
'Basic usage, gap={24}': ( 'Basic usage, gap="large"': (
<Layout.Horizontal gap={24}> <Layout.Horizontal gap="large">
{aButton} {aButton}
{someText} {someText}
{aBox} {aBox}
@@ -287,11 +272,11 @@ const demos: PreviewProps[] = [
function ComponentPreview({title, demos, description, props}: PreviewProps) { function ComponentPreview({title, demos, description, props}: PreviewProps) {
return ( return (
<Card title={title} size="small" type="inner"> <Card title={title} size="small" type="inner">
<Layout.Vertical gap={theme.space.small}> <Layout.Vertical gap="small">
<Text type="secondary">{description}</Text> <Text type="secondary">{description}</Text>
<Collapse ghost> <Collapse ghost>
<Collapse.Panel header="Examples" key="demos"> <Collapse.Panel header="Examples" key="demos">
<Layout.Vertical gap={theme.space.large}> <Layout.Vertical gap="large">
{Object.entries(demos).map(([name, children]) => ( {Object.entries(demos).map(([name, children]) => (
<div key={name}> <div key={name}>
<Tabs type="line"> <Tabs type="line">

View File

@@ -31,13 +31,6 @@ import {errorCounterAtom} from '../chrome/ConsoleLogs';
import {ToplevelProps} from './SandyApp'; import {ToplevelProps} from './SandyApp';
import {useValue} from 'flipper-plugin'; import {useValue} from 'flipper-plugin';
const LeftRailContainer = styled(Layout.Bottom)({
width: 48,
borderRight: `1px solid ${theme.dividerColor}`,
padding: `${theme.paddingLarge}px ${theme.paddingSmall}px`,
});
LeftRailContainer.displayName = 'LeftRailContainer';
const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({ const LeftRailButtonElem = styled(Button)<{kind?: 'small'}>(({kind}) => ({
width: kind === 'small' ? 32 : 36, width: kind === 'small' ? 32 : 36,
height: kind === 'small' ? 32 : 36, height: kind === 'small' ? 32 : 36,
@@ -97,46 +90,48 @@ export function LeftRail({
setToplevelSelection, setToplevelSelection,
}: ToplevelProps) { }: ToplevelProps) {
return ( return (
<LeftRailContainer> <Layout.Container borderRight padv={12} padh={6} width={48}>
<Layout.Vertical center gap={10}> <Layout.Bottom>
<LeftRailButton <Layout.Vertical center gap={10}>
icon={<MobileFilled />} <LeftRailButton
title="App Inspect" icon={<MobileFilled />}
selected={toplevelSelection === 'appinspect'} title="App Inspect"
onClick={() => { selected={toplevelSelection === 'appinspect'}
setToplevelSelection('appinspect'); onClick={() => {
}} setToplevelSelection('appinspect');
/> }}
<LeftRailButton icon={<AppstoreOutlined />} title="Plugin Manager" /> />
<LeftRailButton icon={<BellOutlined />} title="Notifications" /> <LeftRailButton icon={<AppstoreOutlined />} title="Plugin Manager" />
<LeftRailDivider /> <LeftRailButton icon={<BellOutlined />} title="Notifications" />
<DebugLogsButton <LeftRailDivider />
toplevelSelection={toplevelSelection} <DebugLogsButton
setToplevelSelection={setToplevelSelection} toplevelSelection={toplevelSelection}
/> setToplevelSelection={setToplevelSelection}
</Layout.Vertical> />
<Layout.Vertical center gap={10}> </Layout.Vertical>
<LeftRailButton <Layout.Vertical center gap={10}>
icon={<MedicineBoxOutlined />} <LeftRailButton
small icon={<MedicineBoxOutlined />}
title="Setup Doctor" small
/> title="Setup Doctor"
<WelcomeScreenButton /> />
<ShowSettingsButton /> <WelcomeScreenButton />
<LeftRailButton <ShowSettingsButton />
icon={<BugOutlined />} <LeftRailButton
small icon={<BugOutlined />}
title="Feedback / Bug Reporter" small
/> title="Feedback / Bug Reporter"
<LeftRailButton />
icon={<SidebarRight />} <LeftRailButton
small icon={<SidebarRight />}
title="Right Sidebar Toggle" small
/> title="Right Sidebar Toggle"
<LeftSidebarToggleButton /> />
<LeftRailButton icon={<LoginOutlined />} title="Log In" /> <LeftSidebarToggleButton />
</Layout.Vertical> <LeftRailButton icon={<LoginOutlined />} title="Log In" />
</LeftRailContainer> </Layout.Vertical>
</Layout.Bottom>
</Layout.Container>
); );
} }

View File

@@ -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.Vertical borderRight padding={`${theme.space.small}px 0`}> <Layout.Container borderRight padv="small">
{children} <Layout.Vertical>{children}</Layout.Vertical>
</Layout.Vertical> </Layout.Container>
); );
export function SidebarTitle({ export function SidebarTitle({
@@ -36,7 +36,7 @@ export function SidebarTitle({
} }
const LeftMenuTitle = styled(Layout.Horizontal)({ const LeftMenuTitle = styled(Layout.Horizontal)({
padding: `0px ${theme.paddingLarge}px`, padding: `0px ${theme.inlinePaddingH}px`,
lineHeight: `${theme.space.large}px`, lineHeight: `${theme.space.large}px`,
fontSize: theme.fontSize.smallBody, fontSize: theme.fontSize.smallBody,
textTransform: 'uppercase', textTransform: 'uppercase',

View File

@@ -91,11 +91,7 @@ export function SandyApp({logger}: {logger: Logger}) {
setToplevelSelection={setToplevelSelection} setToplevelSelection={setToplevelSelection}
/> />
<Sidebar width={250} minWidth={220} maxWidth={800} gutter> <Sidebar width={250} minWidth={220} maxWidth={800} gutter>
{leftMenuContent && ( {leftMenuContent && leftMenuContent}
<Layout.Container borderRight>
{leftMenuContent}
</Layout.Container>
)}
</Sidebar> </Sidebar>
</Layout.Horizontal> </Layout.Horizontal>
<MainContainer> <MainContainer>

View File

@@ -18,9 +18,9 @@ const {Title, Text, Link} = Typography;
export default function SandyDesignSystem() { export default function SandyDesignSystem() {
return ( return (
<Layout.ScrollContainer className={resetLists}> <Layout.ScrollContainer className={reset}>
<Layout.Vertical gap={theme.space.large}> <Layout.Vertical gap="large">
<Card title="Flipper Design System"> <Card title="Flipper Design System" bordered={false}>
<p> <p>
Welcome to the Flipper Design System. The Flipper design system is Welcome to the Flipper Design System. The Flipper design system is
based on{' '} based on{' '}
@@ -57,7 +57,7 @@ export default function SandyDesignSystem() {
</li> </li>
</ul> </ul>
</Card> </Card>
<Card title="Colors"> <Card title="Colors" bordered={false}>
<Alert message="The following colors are available on the <code>theme</code> object. Please stick to this color palette, as these colors will be translated to dark mode correctly." /> <Alert message="The following colors are available on the <code>theme</code> object. Please stick to this color palette, as these colors will be translated to dark mode correctly." />
<ColorPreview name="primaryColor" /> <ColorPreview name="primaryColor" />
<ColorPreview name="successColor" /> <ColorPreview name="successColor" />
@@ -72,7 +72,7 @@ export default function SandyDesignSystem() {
<ColorPreview name="backgroundTransparentHover" /> <ColorPreview name="backgroundTransparentHover" />
<ColorPreview name="dividerColor" /> <ColorPreview name="dividerColor" />
</Card> </Card>
<Card title="Typography"> <Card title="Typography" bordered={false}>
<Space direction="vertical"> <Space direction="vertical">
<Alert <Alert
message={ message={
@@ -106,7 +106,7 @@ export default function SandyDesignSystem() {
<Input placeholder="Input" /> <Input placeholder="Input" />
</Space> </Space>
</Card> </Card>
<Card title="Theme variables"> <Card title="Theme variables" bordered={false}>
<Alert <Alert
message={ message={
<> <>
@@ -118,7 +118,7 @@ export default function SandyDesignSystem() {
/> />
<pre>{JSON.stringify(theme, null, 2)}</pre> <pre>{JSON.stringify(theme, null, 2)}</pre>
</Card> </Card>
<Card title="Layout components"> <Card title="Layout components" bordered={false}>
<DesignComponentDemos /> <DesignComponentDemos />
</Card> </Card>
</Layout.Vertical> </Layout.Vertical>
@@ -146,7 +146,7 @@ function ColorPreview({name}: {name: keyof typeof theme}) {
); );
} }
const resetLists = css` const reset = css`
ol, ol,
ul { ul {
list-style: revert; list-style: revert;
@@ -155,4 +155,7 @@ const resetLists = css`
.ant-alert { .ant-alert {
margin-bottom: ${theme.space.huge}px; margin-bottom: ${theme.space.huge}px;
} }
.ant-card {
background: transparent;
}
`; `;

View File

@@ -0,0 +1,15 @@
/**
* 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 SandyDesignSystem from './SandyDesignSystem';
export function StyleGuide() {
return <SandyDesignSystem />;
}

View File

@@ -26,8 +26,8 @@ export const theme = {
dividerColor: 'var(--flipper-divider-color)', dividerColor: 'var(--flipper-divider-color)',
borderRadius: 'var(--flipper-border-radius)', borderRadius: 'var(--flipper-border-radius)',
containerBorderRadius: 8, containerBorderRadius: 8,
paddingSmall: 6, // vertical padding on inline elements like buttons inlinePaddingV: 6, // vertical padding on inline elements like buttons
paddingLarge: 12, // horizontal ,,, inlinePaddingH: 12, // horizontal ,,,
space: { space: {
// from Space component in Ant // from Space component in Ant
tiny: 4, tiny: 4,
@@ -51,3 +51,35 @@ export function useIsDarkMode(): boolean {
(state) => state.settingsState.enableSandy && state.settingsState.darkMode, (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

@@ -9,7 +9,13 @@
import React, {CSSProperties} from 'react'; import React, {CSSProperties} from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import {theme} from '../../sandy-chrome/theme'; import {
normalizePadding,
normalizeSpace,
PaddingProps,
Spacing,
theme,
} from '../../sandy-chrome/theme';
import {useIsSandy} from '../../sandy-chrome/SandyContext'; import {useIsSandy} from '../../sandy-chrome/SandyContext';
import {renderLayout} from './LegacyLayout'; import {renderLayout} from './LegacyLayout';
@@ -17,7 +23,6 @@ type ContainerProps = {
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
style?: React.CSSProperties; style?: React.CSSProperties;
padding?: CSSProperties['padding'];
borderBottom?: boolean; borderBottom?: boolean;
borderTop?: boolean; borderTop?: boolean;
borderRight?: boolean; borderRight?: boolean;
@@ -25,7 +30,9 @@ type ContainerProps = {
bordered?: boolean; bordered?: boolean;
rounded?: boolean; rounded?: boolean;
padded?: boolean; padded?: boolean;
}; width?: number;
height?: number;
} & PaddingProps;
const Container = styled.div<ContainerProps>( const Container = styled.div<ContainerProps>(
({ ({
@@ -34,13 +41,16 @@ const Container = styled.div<ContainerProps>(
borderLeft, borderLeft,
borderRight, borderRight,
borderTop, borderTop,
padding,
rounded, rounded,
padded, width,
height,
...rest
}) => ({ }) => ({
width,
height,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
padding: padded ? theme.space.small : padding, padding: normalizePadding(rest),
borderRadius: rounded ? theme.containerBorderRadius : undefined, borderRadius: rounded ? theme.containerBorderRadius : undefined,
flex: 1, flex: 1,
borderStyle: 'solid', borderStyle: 'solid',
@@ -79,7 +89,7 @@ type DistributionProps = ContainerProps & {
/** /**
* Gab between individual items * Gab between individual items
*/ */
gap?: CSSProperties['gap']; gap?: Spacing;
/** /**
* If set, items will be aligned in the center, if false (the default) items will be stretched. * If set, items will be aligned in the center, if false (the default) items will be stretched.
*/ */
@@ -89,14 +99,14 @@ type DistributionProps = ContainerProps & {
const Horizontal = styled(Container)<DistributionProps>(({gap, center}) => ({ const Horizontal = styled(Container)<DistributionProps>(({gap, center}) => ({
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
gap, gap: normalizeSpace(gap, theme.space.small),
alignItems: center ? 'center' : 'stretch', alignItems: center ? 'center' : 'stretch',
})); }));
const Vertical = styled(Container)<DistributionProps>(({gap, center}) => ({ const Vertical = styled(Container)<DistributionProps>(({gap, center}) => ({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap, gap: normalizeSpace(gap, theme.space.small),
alignItems: center ? 'center' : 'stretch', alignItems: center ? 'center' : 'stretch',
})); }));