Template for docs pages

Summary:
- Dynamic loading of documentation pages
- code highlighting for markdown files
- editing playground from sidebar

Reviewed By: emilsjolander

Differential Revision: D6964951

fbshipit-source-id: 1c7f36afa8d23215471d5b9a9c01bd2241c2008e
This commit is contained in:
Daniel Büchele
2018-02-12 10:25:02 -08:00
committed by Facebook Github Bot
parent e43bb9da19
commit 951a429ac5
20 changed files with 321 additions and 149 deletions

View File

@@ -1,4 +1,15 @@
.DocsSidebar {
height: 100%;
width: 350px;
padding: 20px;
border-right: 1px solid #dddfe2;
}
.DocsSidebar h3 {
margin-bottom: 0;
}
.DocsSidebar h4 {
margin-top: 10px;
margin-bottom: 5px;
}

View File

@@ -14,6 +14,13 @@ h2 {
font-weight: 500;
}
.gatsby-highlight pre[class*="language-"] {
background: none;
padding: 0;
font-size: 14px;
margin-bottom: 15px;
}
.Page {
display: flex;
flex-direction: column;
@@ -25,3 +32,7 @@ h2 {
flex-direction: column;
flex-grow: 1;
}
.PageContent.withSpacing {
padding-top: 35px;
}

View File

@@ -14,11 +14,13 @@ import React from 'react';
import Toolbar from './Toolbar';
import Footer from './Footer';
import './Page.css';
require('prismjs/themes/prism.css');
type Props = {|
children: any,
title?: string,
className?: string,
withSpacing?: boolean,
|};
export default (props: Props) => (
@@ -32,7 +34,9 @@ export default (props: Props) => (
<title>Yoga Layout{props.title ? ` | ${props.title}` : ''}</title>
</Head> */}
<Toolbar />
<div className="PageContent">{props.children}</div>
<div className={`PageContent ${props.withSpacing ? 'withSpacing' : ''}`}>
{props.children}
</div>
<Footer />
</div>
);

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
* @format
*/
import React, {Component} from 'react';
import YogaEnumSelect from './YogaEnumSelect';
import YogaPositionEditor from './YogaPositionEditor';
import {Input} from 'antd';
type Props<T, S> = {
property: string,
disabled?: boolean,
value: string | number,
onChange: (value: number) => void,
};
export default (props: Props<T, S>) => {
if (YogaEnumSelect.availableProperties.indexOf(props.property) > -1) {
return <YogaEnumSelect {...props} />;
} else if (
YogaPositionEditor.availableProperties.indexOf(props.property) > -1
) {
return <YogaPositionEditor {...props} />;
} else {
return <Input type="text" {...props} />;
}
};

View File

@@ -11,12 +11,11 @@
*/
import React, {Component} from 'react';
import {Row, Col, Button, Tabs, Input} from 'antd';
import YogaEnumSelect from './YogaEnumSelect';
import {Row, Col, Button, Tabs} from 'antd';
import EditValue from './EditValue';
import type {LayoutRecordT} from './LayoutRecord';
import type {Yoga$Direction} from 'yoga-layout';
import InfoText from './InfoText';
import YogaPositionEditor from './YogaPositionEditor';
import './Editor.css';
const TabPane = Tabs.TabPane;
@@ -64,8 +63,8 @@ export default class Editor extends Component<Props> {
direction
</InfoText>
</h2>
<YogaEnumSelect
property="DIRECTION"
<EditValue
property="direction"
value={this.props.direction}
onChange={e => this.props.onChangeSetting('direction', e)}
/>
@@ -73,9 +72,9 @@ export default class Editor extends Component<Props> {
Flex direction
<InfoText>Defining the direction of the main-axis</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="FLEX_DIRECTION"
property="flexDirection"
value={node ? node.flexDirection : ''}
onChange={e => this.props.onChangeLayout('flexDirection', e)}
/>
@@ -89,7 +88,7 @@ export default class Editor extends Component<Props> {
up, relative to it's siblings
</InfoText>
</h2>
<Input
<EditValue
type="text"
disabled={disabled || selectedNodeIsRoot}
value={node ? node.flexGrow : ''}
@@ -106,7 +105,7 @@ export default class Editor extends Component<Props> {
anymore.
</InfoText>
</h2>
<Input
<EditValue
type="text"
disabled={disabled || selectedNodeIsRoot}
value={node ? node.flexShrink : ''}
@@ -123,9 +122,9 @@ export default class Editor extends Component<Props> {
Wrapping behaviour when child nodes don't fit into a single line
</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="WRAP"
property="flexWrap"
value={node ? node.flexWrap : ''}
onChange={e => this.props.onChangeLayout('flexWrap', e)}
/>
@@ -135,9 +134,9 @@ export default class Editor extends Component<Props> {
Justify content
<InfoText>Aligns child nodes along the main-axis</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="JUSTIFY"
property="justifyContent"
value={node ? node.justifyContent : ''}
onChange={e => this.props.onChangeLayout('justifyContent', e)}
/>
@@ -146,9 +145,9 @@ export default class Editor extends Component<Props> {
Align items
<InfoText>Aligns child nodes along the cross-axis</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="ALIGN"
property="alignItems"
value={node ? node.alignItems : ''}
onChange={e => this.props.onChangeLayout('alignItems', e)}
/>
@@ -159,9 +158,9 @@ export default class Editor extends Component<Props> {
Specifies alignment on the cross-axis for the node itself
</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled || selectedNodeIsRoot}
property="ALIGN"
property="alignSelf"
value={node ? node.alignSelf : ''}
onChange={e => this.props.onChangeLayout('alignSelf', e)}
/>
@@ -172,9 +171,9 @@ export default class Editor extends Component<Props> {
Alignment of lines along the cross-axis when wrapping
</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="ALIGN"
property="alignContent"
value={node ? node.alignContent : ''}
onChange={e => this.props.onChangeLayout('alignContent', e)}
/>
@@ -186,7 +185,7 @@ export default class Editor extends Component<Props> {
</h2>
<Row gutter={15}>
<Col span={12}>
<Input
<EditValue
type="text"
placeholder="width"
disabled={disabled}
@@ -197,7 +196,7 @@ export default class Editor extends Component<Props> {
/>
</Col>
<Col span={12}>
<Input
<EditValue
type="text"
placeholder="height"
disabled={disabled}
@@ -216,7 +215,7 @@ export default class Editor extends Component<Props> {
displaying videos
</InfoText>
</h2>
<Input
<EditValue
type="text"
placeholder="Aspect ratio"
disabled={disabled}
@@ -228,7 +227,7 @@ export default class Editor extends Component<Props> {
<h2>Box model</h2>
{['padding', 'border', 'margin'].map(property => (
<YogaPositionEditor
<EditValue
property={property}
key={property}
value={node ? node[property] : undefined}
@@ -244,13 +243,13 @@ export default class Editor extends Component<Props> {
</InfoText>
</h2>
<YogaEnumSelect
<EditValue
disabled={disabled}
property="POSITION_TYPE"
property="positionType"
value={node ? node.positionType : ''}
onChange={e => this.props.onChangeLayout('positionType', e)}
/>
<YogaPositionEditor
<EditValue
property="position"
value={node ? node.position : undefined}
onChange={value => this.props.onChangeLayout('position', value)}

View File

@@ -1,4 +1,4 @@
.YogaEnumSelect.ant-radio-group {
.YogaEnumSelect {
display: flex;
}
@@ -7,3 +7,8 @@
flex-basis: 0;
text-align: center;
}
.YogaEnumSelect .ant-btn {
flex-grow: 1;
flex-basis: 0;
}

View File

@@ -17,18 +17,32 @@ import './YogaEnumSelect.css';
const RadioButton = Radio.Button;
const RadioGroup = Radio.Group;
const PROPERTY_LOOKUP = {
flexDirection: 'FLEX_DIRECTION',
direction: 'DIRECTION',
justifyContent: 'JUSTIFY',
alignSelf: 'ALIGN',
alignContent: 'ALIGN',
alignItems: 'ALIGN',
positionType: 'POSITION_TYPE',
flexWrap: 'WRAP',
};
type Props = {
property: string,
property: $Keys<typeof PROPERTY_LOOKUP>,
disabled?: boolean,
value: string | number,
onChange: (value: number) => void,
};
export default class YogaEnumSelect extends Component<Props> {
static availableProperties = Object.keys(PROPERTY_LOOKUP);
values: Array<{key: string, value: number}>;
constructor({property}: Props) {
super();
property = PROPERTY_LOOKUP[property];
// $FlowFixMe
this.values = Object.keys(yoga)
.map(key => ({key, value: yoga[key]}))
@@ -42,29 +56,32 @@ export default class YogaEnumSelect extends Component<Props> {
};
render() {
const {property, ...props} = this.props;
let {property, ...props} = this.props;
property = PROPERTY_LOOKUP[property];
const selected = this.values.find(
({key, value}) => value === this.props.value,
);
const replacer = new RegExp(`^${property}_`);
return this.values.length > 3 ? (
<Dropdown
disabled={props.disabled}
overlay={
<Menu onClick={this.handleMenuClick}>
{this.values.map(({key, value}) => (
<Menu.Item key={key} value={value}>
{key.replace(replacer, '')}
</Menu.Item>
))}
</Menu>
}>
<Button>
{selected ? selected.key.replace(replacer, '') : 'undefiend'}{' '}
<Icon type="down" />
</Button>
</Dropdown>
<div className="YogaEnumSelect">
<Dropdown
disabled={props.disabled}
overlay={
<Menu onClick={this.handleMenuClick}>
{this.values.map(({key, value}) => (
<Menu.Item key={key} value={value}>
{key.replace(replacer, '')}
</Menu.Item>
))}
</Menu>
}>
<Button>
{selected ? selected.key.replace(replacer, '') : ''}
<Icon type="down" />
</Button>
</Dropdown>
</div>
) : (
<RadioGroup
{...props}

View File

@@ -18,11 +18,13 @@ import './YogaPositionEditor.css';
type Props = {
value: PositionRecordT,
property: string,
property: 'position' | 'margin' | 'padding' | 'border',
onChange: (value: PositionRecordT) => void,
};
export default class YogaPositionEditor extends Component<Props> {
static availableProperties = ['position', 'margin', 'padding', 'border'];
static defaultProps = {
value: PositionRecord(),
};

View File

@@ -34,6 +34,7 @@ type Props = {
showGuides: boolean,
className?: string,
height?: string | number,
persist?: boolean,
renderSidebar?: (layoutDefinition: LayoutRecordT, onChange: Function) => any,
};
@@ -53,7 +54,7 @@ export default class Playground extends Component<Props, State> {
static defaultProps = {
layoutDefinition: LayoutRecord({
width: 800,
width: 700,
height: 400,
justifyContent: yoga.JUSTIFY_SPACE_BETWEEN,
alignItems: yoga.ALIGN_FLEX_START,
@@ -65,14 +66,15 @@ export default class Playground extends Component<Props, State> {
bottom: '10',
}),
margin: PositionRecord({
left: '20',
top: '70',
left: '30',
top: '30',
}),
}),
direction: yoga.DIRECTION_LTR,
maxDepth: 3,
showCode: false,
showGuides: true,
persist: false,
};
state = {
@@ -144,8 +146,9 @@ export default class Playground extends Component<Props, State> {
const {selectedNodePath, layoutDefinition} = this.state;
if (selectedNodePath) {
const index = selectedNodePath.pop();
const path = getPath(selectedNodePath).concat('children');
const updatedChildren = layoutDefinition.getIn(path).delete(index);
const updatedChildren = layoutDefinition
.getIn(getPath(selectedNodePath))
.delete(index);
this.modifyAtPath(path, updatedChildren);
this.setState({selectedNodePath: null});
}
@@ -154,8 +157,9 @@ export default class Playground extends Component<Props, State> {
onAdd = () => {
const {selectedNodePath, layoutDefinition} = this.state;
if (selectedNodePath) {
const path = getPath(selectedNodePath).concat('children');
const updatedChildren = layoutDefinition.getIn(path).push(LayoutRecord());
const updatedChildren = layoutDefinition
.getIn(getPath(selectedNodePath))
.push(LayoutRecord());
this.modifyAtPath(
path,
updatedChildren,
@@ -177,9 +181,11 @@ export default class Playground extends Component<Props, State> {
selectedNodePath,
});
window.location.hash = btoa(
JSON.stringify(this.removeUnchangedProperties(layoutDefinition)),
);
if (this.props.persist) {
window.location.hash = btoa(
JSON.stringify(this.removeUnchangedProperties(layoutDefinition)),
);
}
}
removeUnchangedProperties = (node: LayoutRecordT): Object => {
@@ -306,7 +312,7 @@ export default class Playground extends Component<Props, State> {
<div className={`PlaygroundContainer ${this.props.className || ''}`}>
<div>
{this.props.renderSidebar(
this.state.layoutDefinition,
layoutDefinition.getIn(getPath(selectedNodePath)),
this.onChangeLayout,
)}
</div>

View File

@@ -16,8 +16,15 @@ import Page from '../../components/Page';
import Padded from '../../components/Padded';
import {Row, Col} from 'antd';
export default () => (
<Page>
const CATEGORY_TITLE = {
'getting-started': 'Getting Started',
properties: 'Properties',
examples: 'Examples',
contributing: 'Contributing',
};
export default ({data}) => (
<Page withSpacing>
<Padded>
<h1>Documentation</h1>
<p>
@@ -29,65 +36,41 @@ export default () => (
nec leo pharetra fermentum.
</p>
<Row>
<Col md={6} sm={12} xs={24}>
<h2>Getting Started</h2>
<Link to="">Litho</Link>
<br />
<Link to="">ComponentKit</Link>
<br />
<Link to="">React Native</Link>
<br />
<Link to="">Addin Yoga to a project</Link>
</Col>
<Col md={6} sm={12} xs={24}>
<h2>Properties</h2>
<Link to="">Width and height</Link>
<br />
<Link to="">Max/min width and height</Link>
<br />
<Link to="docs/flexDirection">Flex direction</Link>
<br />
<Link to="">Justify content</Link>
<br />
<Link to="">Align items/self</Link>
<br />
<Link to="">Flex basis, grow and shrink</Link>
<br />
<Link to="">Margins, paddings, and borders</Link>
<br />
<Link to="">Flex wrap</Link>
<br />
<Link to="">Align content</Link>
<br />
<Link to="">Absolute layout</Link>
<br />
<Link to="">Aspect ratio</Link>
<br />
<Link to="">Layout direction</Link>
</Col>
<Col md={6} sm={12} xs={24}>
<h2>Examples</h2>
<Link to="">Overlays</Link>
<br />
<Link to="">Floating buttons</Link>
<br />
<Link to="">Flexible text</Link>
</Col>
<Col md={6} sm={12} xs={24}>
<h2>Contributing</h2>
<Link to="">Check out the code</Link>
<br />
<Link to="">Running the test suite</Link>
<br />
<Link to="">Making a change</Link>
<br />
<Link to="">Adding a test</Link>
<br />
<Link to="">Adding documentation</Link>
<br />
<Link to="">Opening a pull request</Link>
</Col>
{['getting-started', 'properties', 'examples', 'contributing'].map(
category => (
<Col md={6} sm={12} xs={24} key={category}>
<h2>{CATEGORY_TITLE[category]}</h2>
{data.allMarkdownRemark.edges
.filter(
({node}) =>
node.fileAbsolutePath.indexOf(`/${category}/`) > -1,
)
.map(({node}) => (
<Link key={node.id} to={node.frontmatter.path}>
{node.frontmatter.title}
</Link>
))}
</Col>
),
)}
</Row>
</Padded>
</Page>
);
export const query = graphql`
query IndexQuery {
allMarkdownRemark {
edges {
node {
id
frontmatter {
title
path
}
fileAbsolutePath
}
}
}
}
`;

View File

@@ -16,6 +16,6 @@ import Playground from '../components/Playground';
export default () => (
<Page title="Playground">
<Playground height="100%" />
<Playground height="100%" selectedNodePath={[]} persist />
</Page>
);

View File

@@ -14,25 +14,45 @@ import React from 'react';
import Page from '../components/Page';
import Playground from '../components/Playground';
import DocsSidebar from '../components/DocsSidebar';
import YogaEnumSelect from '../components/Playground/YogaEnumSelect';
import EditValue from '../components/Playground/EditValue';
import Link from 'gatsby-link';
import {Button, Icon, Row, Col} from 'antd';
export default ({pathContext}) => {
return (
<Page>
<Playground
selectedNodePath={[]}
showGuides={false}
renderSidebar={(layout, onChange) => (
<DocsSidebar>
<div dangerouslySetInnerHTML={{__html: pathContext.html}} />
<YogaEnumSelect
property="FLEX_DIRECTION"
value={layout.flexDirection}
onChange={e => onChange('flexDirection', e)}
/>
</DocsSidebar>
)}
/>
</Page>
);
};
export default ({pathContext}) => (
<Page>
<Playground
selectedNodePath={[]}
showGuides={false}
renderSidebar={(layout, onChange) => (
<DocsSidebar>
<Link to="/docs">
<Icon type="left-circle-o" /> back to overview
</Link>
<div dangerouslySetInnerHTML={{__html: pathContext.html}} />
{pathContext.frontmatter.editableProperties && (
<Row type="flex" align="bottom">
<Col span={12}>
<h3>Try it out</h3>
</Col>
<Col span={12}>
<Link to="/playground">
<Icon type="export" /> Open in playground
</Link>
</Col>
</Row>
)}
{(pathContext.frontmatter.editableProperties || []).map(prop => (
<div key={prop}>
<h4>{prop}</h4>
<EditValue
property={prop}
value={layout[prop]}
onChange={e => onChange(prop, e)}
/>
</div>
))}
</DocsSidebar>
)}
/>
</Page>
);

View File

@@ -13,11 +13,16 @@
import React from 'react';
import Page from '../components/Page';
import Padded from '../components/Padded';
import {Icon} from 'antd';
import Link from 'gatsby-link';
export default ({pathContext}) => {
return (
<Page>
<Page withSpacing>
<Padded>
<Link to="/docs">
<Icon type="left-circle-o" /> back to overview
</Link>
<div dangerouslySetInnerHTML={{__html: pathContext.html}} />
</Padded>
</Page>