website v2 skeleton
Summary: Sets up a skeleton for the new yoga website using gatsby static site generator allow-large-files Reviewed By: emilsjolander Differential Revision: D6952326 fbshipit-source-id: 7579bc80bec21552689da5b78f3d960910ff13bb
This commit is contained in:
committed by
Facebook Github Bot
parent
b08bd572ef
commit
e43bb9da19
8
website/.gitignore
vendored
Normal file
8
website/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Project dependencies
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
.cache/
|
||||
# Build directory
|
||||
public/
|
||||
.DS_Store
|
||||
yarn-error.log
|
6
website/README.md
Normal file
6
website/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Yoga documentation and playground
|
||||
|
||||
This uses [gatsby.js](https://www.gatsbyjs.org/).
|
||||
|
||||
- Run `yarn develop` during development
|
||||
- Run `yarn build` to generate site
|
17
website/docs/flexDirection.md
Normal file
17
website/docs/flexDirection.md
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
path: "docs/flexDirection"
|
||||
title: "Flex Direction"
|
||||
hasPlayground: true
|
||||
---
|
||||
|
||||
## Flex Direction
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nec
|
||||
sodales libero, sit amet tempus diam. Vivamus finibus vestibulum
|
||||
est. Vestibulum feugiat pellentesque diam vel hendrerit. Nunc
|
||||
pretium sollicitudin magna sed pharetra.
|
||||
|
||||
Duis bibendum dapibus quam ac rutrum. Suspendisse potenti. Aliquam
|
||||
est sapien, gravida ac turpis iaculis, convallis rutrum justo. Sed
|
||||
est augue, pellentesque eleifend mauris non, ultrices aliquam purus.
|
||||
Duis sed lorem a lectus feugiat fringilla eu non elit.
|
43
website/gatsby-config.js
Normal file
43
website/gatsby-config.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
siteMetadata: {
|
||||
title: 'Gatsby Default Starter',
|
||||
},
|
||||
plugins: [
|
||||
'gatsby-plugin-react-next',
|
||||
'gatsby-plugin-react-helmet',
|
||||
{
|
||||
resolve: `gatsby-plugin-less`,
|
||||
options: {
|
||||
theme: {
|
||||
'primary-color': '#95DDCF',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: 'gatsby-plugin-antd',
|
||||
options: {
|
||||
style: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: `gatsby-source-filesystem`,
|
||||
options: {
|
||||
path: `${__dirname}/docs`,
|
||||
name: 'markdown-pages',
|
||||
},
|
||||
},
|
||||
'gatsby-transformer-remark',
|
||||
],
|
||||
};
|
49
website/gatsby-node.js
Normal file
49
website/gatsby-node.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
exports.createPages = ({boundActionCreators, graphql}) => {
|
||||
const {createPage} = boundActionCreators;
|
||||
const withPlayground = path.resolve(`src/templates/withPlayground.js`);
|
||||
const withoutPlayground = path.resolve(`src/templates/withoutPlayground.js`);
|
||||
|
||||
return graphql(`
|
||||
{
|
||||
allMarkdownRemark {
|
||||
edges {
|
||||
node {
|
||||
frontmatter {
|
||||
path
|
||||
hasPlayground
|
||||
}
|
||||
html
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`).then(result => {
|
||||
if (result.errors) {
|
||||
return Promise.reject(result.errors);
|
||||
}
|
||||
|
||||
result.data.allMarkdownRemark.edges.forEach(({node}) => {
|
||||
createPage({
|
||||
path: node.frontmatter.path,
|
||||
component: node.frontmatter.hasPlayground
|
||||
? withPlayground
|
||||
: withoutPlayground,
|
||||
context: node,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
12381
website/package-lock.json
generated
Normal file
12381
website/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
35
website/package.json
Normal file
35
website/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "gatsby-starter-default",
|
||||
"description": "Gatsby default starter",
|
||||
"version": "1.0.0",
|
||||
"author": "Kyle Mathews <mathews.kyle@gmail.com>",
|
||||
"dependencies": {
|
||||
"antd": "^3.2.0",
|
||||
"gatsby": "^1.9.158",
|
||||
"gatsby-link": "^1.6.34",
|
||||
"gatsby-plugin-antd": "^1.0.10",
|
||||
"gatsby-plugin-less": "^1.1.4",
|
||||
"gatsby-plugin-react-helmet": "^2.0.3",
|
||||
"gatsby-plugin-react-next": "^1.0.8",
|
||||
"gatsby-source-filesystem": "^1.5.18",
|
||||
"gatsby-transformer-remark": "^1.7.31",
|
||||
"immutable": "^4.0.0-rc.9",
|
||||
"react-helmet": "^5.2.0",
|
||||
"react-syntax-highlighter": "^7.0.0",
|
||||
"yoga-layout": "^1.9.0"
|
||||
},
|
||||
"keywords": [
|
||||
"gatsby"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "n/a",
|
||||
"scripts": {
|
||||
"build": "gatsby build",
|
||||
"develop": "gatsby develop",
|
||||
"format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"src/**/*.js\"",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^1.10.2"
|
||||
}
|
||||
}
|
4
website/src/components/DocsSidebar.css
Normal file
4
website/src/components/DocsSidebar.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.DocsSidebar {
|
||||
width: 350px;
|
||||
padding: 20px;
|
||||
}
|
22
website/src/components/DocsSidebar.js
Normal file
22
website/src/components/DocsSidebar.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import './DocsSidebar.css';
|
||||
|
||||
type Props = {
|
||||
children: any,
|
||||
};
|
||||
|
||||
export default (props: Props) => (
|
||||
<div className="DocsSidebar">{props.children}</div>
|
||||
);
|
10
website/src/components/Footer.css
Normal file
10
website/src/components/Footer.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.Footer {
|
||||
background: #1c2126;
|
||||
min-height: 100px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.Footer a {
|
||||
color: white;
|
||||
}
|
21
website/src/components/Footer.js
Normal file
21
website/src/components/Footer.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Padded from './Padded';
|
||||
import './Footer.css';
|
||||
|
||||
export default () => (
|
||||
<footer className="Footer">
|
||||
<Padded>footer</Padded>
|
||||
</footer>
|
||||
);
|
7
website/src/components/Padded.css
Normal file
7
website/src/components/Padded.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.Padded {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
padding: 0 20px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
23
website/src/components/Padded.js
Normal file
23
website/src/components/Padded.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import './Padded.css';
|
||||
|
||||
type Props = {
|
||||
children: any,
|
||||
className?: string,
|
||||
};
|
||||
|
||||
export default (props: Props) => (
|
||||
<div className={`Padded ${props.className || ''}`}>{props.children}</div>
|
||||
);
|
27
website/src/components/Page.css
Normal file
27
website/src/components/Page.css
Normal file
@@ -0,0 +1,27 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Barlow');
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Barlow', sans-serif;
|
||||
color: #303845;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.Page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.PageContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
38
website/src/components/Page.js
Normal file
38
website/src/components/Page.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Toolbar from './Toolbar';
|
||||
import Footer from './Footer';
|
||||
import './Page.css';
|
||||
|
||||
type Props = {|
|
||||
children: any,
|
||||
title?: string,
|
||||
className?: string,
|
||||
|};
|
||||
|
||||
export default (props: Props) => (
|
||||
<div className={`Page ${props.className || ''}`}>
|
||||
{/* <Head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdnjs.cloudflare.com/ajax/libs/antd/3.2.0/antd.min.css"
|
||||
/>
|
||||
<link href="//fonts.googleapis.com/css?family=Barlow" rel="stylesheet" />
|
||||
<title>Yoga Layout{props.title ? ` | ${props.title}` : ''}</title>
|
||||
</Head> */}
|
||||
<Toolbar />
|
||||
<div className="PageContent">{props.children}</div>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
20
website/src/components/Playground/Code.css
Normal file
20
website/src/components/Playground/Code.css
Normal file
@@ -0,0 +1,20 @@
|
||||
.Code {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.CodeContainer {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.CodeLanguageSelector.ant-radio-group {
|
||||
display: flex;
|
||||
margin: 10px 15px;
|
||||
}
|
||||
|
||||
.CodeLanguageSelector.ant-radio-group .ant-radio-button-wrapper {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
text-align: center;
|
||||
}
|
114
website/src/components/Playground/Code.js
Normal file
114
website/src/components/Playground/Code.js
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* 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 SyntaxHighlighter, {
|
||||
registerLanguage,
|
||||
} from 'react-syntax-highlighter/prism-light';
|
||||
import styles from 'react-syntax-highlighter/styles/prism/prism';
|
||||
import jsx from 'react-syntax-highlighter/languages/prism/jsx';
|
||||
//import javascript from 'react-syntax-highlighter/languages/prism/javascript';
|
||||
import java from 'react-syntax-highlighter/languages/prism/java';
|
||||
import objectivec from 'react-syntax-highlighter/languages/prism/objectivec';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import {Radio} from 'antd';
|
||||
import CodeJavaScript from './CodeJavaScript';
|
||||
import CodeLitho from './CodeLitho';
|
||||
import CodeReactNative from './CodeReactNative';
|
||||
import CodeComponentKit from './CodeComponentKit';
|
||||
import './Code.css';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
const RadioButton = Radio.Button;
|
||||
const RadioGroup = Radio.Group;
|
||||
registerLanguage('jsx', jsx);
|
||||
//registerLanguage('javascript', javascript);
|
||||
registerLanguage('java', java);
|
||||
registerLanguage('objectivec', objectivec);
|
||||
|
||||
type Language = 'JavaScript' | 'Litho' | 'ComponentKit' | 'React Native';
|
||||
|
||||
type Props = {
|
||||
layoutDefinition: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
languages: Array<Language>,
|
||||
};
|
||||
|
||||
type State = {
|
||||
language: Language,
|
||||
};
|
||||
|
||||
export default class Code extends Component<Props, State> {
|
||||
static defaultProps = {
|
||||
languages: [/*'JavaScript', */ 'Litho', 'ComponentKit', 'React Native'],
|
||||
};
|
||||
|
||||
state = {
|
||||
language: 'Litho',
|
||||
};
|
||||
|
||||
generateCode(lang: Language): string {
|
||||
if (lang === 'JavaScript') {
|
||||
return CodeJavaScript(this.props.layoutDefinition, this.props.direction);
|
||||
} else if (lang === 'Litho') {
|
||||
return CodeLitho(this.props.layoutDefinition, this.props.direction);
|
||||
} else if (lang === 'ComponentKit') {
|
||||
return CodeComponentKit(
|
||||
this.props.layoutDefinition,
|
||||
this.props.direction,
|
||||
);
|
||||
} else if (lang === 'React Native') {
|
||||
return CodeReactNative(this.props.layoutDefinition, this.props.direction);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
getLanguage(): string {
|
||||
if (this.state.language === 'Litho') {
|
||||
return 'java';
|
||||
} else if (this.state.language === 'React Native') {
|
||||
return 'javascript';
|
||||
} else if (this.state.language === 'ComponentKit') {
|
||||
return 'objectivec';
|
||||
} else {
|
||||
return this.state.language;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="Code">
|
||||
<RadioGroup
|
||||
className="CodeLanguageSelector"
|
||||
onChange={e => this.setState({language: e.target.value})}
|
||||
value={this.state.language}>
|
||||
{this.props.languages.map(lang => (
|
||||
<RadioButton key={lang} value={lang}>
|
||||
{lang}
|
||||
</RadioButton>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<div className="CodeContainer">
|
||||
<SyntaxHighlighter
|
||||
language={this.getLanguage()}
|
||||
style={styles}
|
||||
customStyle={{fontSize: '13px', backgroundColor: 'white'}}
|
||||
lineNumberStyle={{userSelect: 'none', opacity: 0.5}}
|
||||
codeTagProps={{style: {tabSize: 4}}}
|
||||
showLineNumbers>
|
||||
{this.generateCode(this.state.language)}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
184
website/src/components/Playground/CodeComponentKit.js
Normal file
184
website/src/components/Playground/CodeComponentKit.js
Normal file
@@ -0,0 +1,184 @@
|
||||
// @flow
|
||||
import yoga from 'yoga-layout';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction /* Yoga$Node */} from 'yoga-layout';
|
||||
|
||||
type Yoga$Node = any;
|
||||
|
||||
const enumLookup = {
|
||||
flexDirection: {
|
||||
[yoga.FLEX_DIRECTION_COLUMN]: 'CKFlexboxDirectionVertical',
|
||||
[yoga.FLEX_DIRECTION_ROW]: 'CKFlexboxDirectionHorizontal',
|
||||
[yoga.FLEX_DIRECTION_COLUMN_REVERSE]: 'CKFlexboxDirectionVerticalReverse',
|
||||
[yoga.FLEX_DIRECTION_ROW_REVERSE]: 'CKFlexboxDirectionHorizontalReverse',
|
||||
},
|
||||
alignItems: {
|
||||
[yoga.ALIGN_FLEX_START]: 'CKFlexboxAlignItemsStart',
|
||||
[yoga.ALIGN_FLEX_END]: 'CKFlexboxAlignItemsEnd',
|
||||
[yoga.ALIGN_CENTER]: 'CKFlexboxAlignItemsCenter',
|
||||
[yoga.ALIGN_BASELINE]: 'CKFlexboxAlignItemsBaseline',
|
||||
[yoga.ALIGN_STRETCH]: 'CKFlexboxAlignItemsStretch',
|
||||
},
|
||||
alignSelf: {
|
||||
[yoga.ALIGN_AUTO]: 'CKFlexboxAlignSelfAuto',
|
||||
[yoga.ALIGN_FLEX_START]: 'CKFlexboxAlignSelfStart',
|
||||
[yoga.ALIGN_FLEX_END]: 'CKFlexboxAlignSelfEnd',
|
||||
[yoga.ALIGN_CENTER]: 'CKFlexboxAlignSelfCenter',
|
||||
[yoga.ALIGN_BASELINE]: 'CKFlexboxAlignSelfBaseline',
|
||||
[yoga.ALIGN_STRETCH]: 'CKFlexboxAlignSelfStretch',
|
||||
},
|
||||
alignContent: {
|
||||
[yoga.ALIGN_FLEX_START]: 'CKFlexboxAlignContentStart',
|
||||
[yoga.ALIGN_FLEX_END]: 'CKFlexboxAlignContentEnd',
|
||||
[yoga.ALIGN_CENTER]: 'CKFlexboxAlignContentCenter',
|
||||
[yoga.ALIGN_SPACE_BETWEEN]: 'CKFlexboxAlignContentSpaceBetween',
|
||||
[yoga.ALIGN_SPACE_AROUND]: 'CKFlexboxAlignContentSpaceAround',
|
||||
[yoga.ALIGN_STRETCH]: 'CKFlexboxAlignContentStretch',
|
||||
},
|
||||
justifyContent: {
|
||||
[yoga.JUSTIFY_FLEX_START]: 'CKFlexboxJustifyContentStart',
|
||||
[yoga.JUSTIFY_CENTER]: 'CKFlexboxJustifyContentCenter',
|
||||
[yoga.JUSTIFY_FLEX_END]: 'CKFlexboxJustifyContentEnd',
|
||||
[yoga.JUSTIFY_SPACE_BETWEEN]: 'CKFlexboxJustifyContentSpaceBetween',
|
||||
[yoga.JUSTIFY_SPACE_AROUND]: 'CKFlexboxJustifyContentSpaceAround',
|
||||
},
|
||||
flexWrap: {
|
||||
[yoga.WRAP_NO_WRAP]: 'CKFlexboxWrapNoWrap',
|
||||
[yoga.WRAP_WRAP]: 'CKFlexboxWrapWrap',
|
||||
[yoga.WRAP_WRAP_REVERSE]: 'CKFlexboxWrapWrapReverse',
|
||||
},
|
||||
positionType: {
|
||||
[yoga.POSITION_TYPE_RELATIVE]: 'CKFlexboxPositionTypeRelative',
|
||||
[yoga.POSITION_TYPE_ABSOLUTE]: 'CKFlexboxPositionTypeAbsolute',
|
||||
},
|
||||
};
|
||||
|
||||
const untouchedLayout = LayoutRecord({});
|
||||
const untouchedPosition = PositionRecord({});
|
||||
|
||||
function keyLookup(key: string): string {
|
||||
const keyLookup = {
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: 'direction',
|
||||
};
|
||||
return keyLookup[key] || key;
|
||||
}
|
||||
|
||||
function getLayoutCode(
|
||||
node: LayoutRecordT,
|
||||
indent: string = '',
|
||||
isRoot?: boolean,
|
||||
): string {
|
||||
const lines = [];
|
||||
const isFlexbox = node.children.size > 0;
|
||||
|
||||
lines.push(
|
||||
indent +
|
||||
`${isRoot ? '' : `.component = \n${indent}`}[${
|
||||
isFlexbox ? 'CKFlexboxComponent' : 'CKComponent'
|
||||
}`,
|
||||
);
|
||||
lines.push(indent + ` newWithView:{}`);
|
||||
lines.push(indent + ` size:{${node.width},${node.height}}`);
|
||||
|
||||
const CKFlexboxComponentStyle = [
|
||||
'direction',
|
||||
'margin',
|
||||
'justifyContent',
|
||||
'alignItems',
|
||||
'alignContent',
|
||||
'wrap',
|
||||
'padding',
|
||||
'border',
|
||||
];
|
||||
const CKFlexboxComponentChild = [
|
||||
'margin',
|
||||
'padding',
|
||||
'flexGrow',
|
||||
'flexShrink',
|
||||
'flexBasis',
|
||||
'alignSelf',
|
||||
'position',
|
||||
];
|
||||
|
||||
if (isFlexbox) {
|
||||
// render styles
|
||||
lines.push(indent + ` style:{`);
|
||||
indent += '\t';
|
||||
CKFlexboxComponentStyle.forEach(key => {
|
||||
let line = renderKey(node, key, indent);
|
||||
if (line) {
|
||||
lines.push(line);
|
||||
}
|
||||
});
|
||||
indent = indent.substr(-1);
|
||||
lines.push(indent + ` }`);
|
||||
|
||||
// render children
|
||||
lines.push(indent + ' children:{');
|
||||
lines.push(
|
||||
...node.children
|
||||
.toJSON()
|
||||
.map(
|
||||
child =>
|
||||
`${indent}\t{\n${getLayoutCode(
|
||||
child,
|
||||
indent + '\t\t',
|
||||
)}\n${indent}\t},`,
|
||||
),
|
||||
);
|
||||
lines.push(indent + `}]${isRoot ? ';' : ''}`);
|
||||
} else {
|
||||
lines[lines.length - 1] += ']';
|
||||
CKFlexboxComponentChild.forEach(key => {
|
||||
let line = renderKey(node, key, indent);
|
||||
if (line) {
|
||||
lines.push(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
function renderKey(node: Yoga$Node, key: string, indent: string): ?string {
|
||||
if (
|
||||
node[key] instanceof PositionRecord &&
|
||||
!node[key].equals(untouchedPosition)
|
||||
) {
|
||||
const lines = [];
|
||||
lines.push(indent + `.${key} = {`);
|
||||
|
||||
if (key === 'position') {
|
||||
lines.push(
|
||||
indent + `\t.type = ${enumLookup.positionType[node.positionType]},`,
|
||||
);
|
||||
}
|
||||
|
||||
['top', 'left', 'right', 'bottom'].forEach(pKey => {
|
||||
if (node[key][pKey]) {
|
||||
lines.push(indent + `\t.${pKey} = ${node[key][pKey]},`);
|
||||
}
|
||||
});
|
||||
|
||||
lines.push(indent + `},`);
|
||||
return lines.join('\n');
|
||||
} else if (node[key] !== untouchedLayout[key]) {
|
||||
if (enumLookup[key]) {
|
||||
return indent + `.${keyLookup(key)} = ${enumLookup[key][node[key]]},`;
|
||||
} else {
|
||||
console.error(`Unknown property ${key}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function generateCode(
|
||||
root: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
): string {
|
||||
return ['CKFlexboxComponent *c =', getLayoutCode(root, '\t', true)].join(
|
||||
'\n',
|
||||
);
|
||||
}
|
138
website/src/components/Playground/CodeJavaScript.js
Normal file
138
website/src/components/Playground/CodeJavaScript.js
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 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 yoga from 'yoga-layout';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
|
||||
export const JSEnumLookup = {
|
||||
justifyContent: 'JUSTIFY_',
|
||||
alignItems: 'ALIGN_',
|
||||
alignContent: 'ALIGN_',
|
||||
alignSelf: 'ALIGN_',
|
||||
position: 'POSITION_',
|
||||
flexDirection: 'DIRECTION_',
|
||||
flexWrap: 'WRAP_',
|
||||
positionType: 'POSITION_TYPE_',
|
||||
direction: 'DIRECTION_',
|
||||
};
|
||||
|
||||
function getEnum(yogaEnum: string, value: string | number): string {
|
||||
return `yoga.${Object.keys(yoga)
|
||||
.filter(key => key.toLowerCase().startsWith(yogaEnum.toLowerCase()))
|
||||
.find(key => yoga[key] === value) || value}`;
|
||||
}
|
||||
|
||||
function setProperty(
|
||||
name: string,
|
||||
key: string,
|
||||
value: string,
|
||||
enumValue?: string,
|
||||
): string {
|
||||
return [
|
||||
name,
|
||||
'.set',
|
||||
key[0].toUpperCase() + key.substr(1),
|
||||
'(',
|
||||
enumValue ? `${enumValue}, ` : '',
|
||||
JSEnumLookup[key] ? getEnum(JSEnumLookup[key], value) : value,
|
||||
');',
|
||||
].join('');
|
||||
}
|
||||
|
||||
function getLayoutCode(
|
||||
node: LayoutRecordT,
|
||||
name: string,
|
||||
index: number,
|
||||
): string {
|
||||
const lines = [];
|
||||
const childName = (i: number) => `${name === 'root' ? 'node' : name}_${i}`;
|
||||
|
||||
lines.push(
|
||||
...node.children.map((node, i) => getLayoutCode(node, childName(i), i)),
|
||||
);
|
||||
|
||||
lines.push('', `// create node ${name}`, `const ${name} = Node.create();`);
|
||||
const untouchedNode = LayoutRecord({width: '', height: ''});
|
||||
Object.keys(untouchedNode.toJS()).forEach(key => {
|
||||
if (key !== 'children' && untouchedNode[key] !== node[key]) {
|
||||
if (node[key] instanceof PositionRecord) {
|
||||
// iterate through position record
|
||||
const {top, left, right, bottom} = node[key].toJS();
|
||||
if (
|
||||
top !== untouchedNode[key].top &&
|
||||
top === left &&
|
||||
top === right &&
|
||||
top === bottom
|
||||
) {
|
||||
// all edges
|
||||
lines.push(setProperty(name, key, node[key].top, getEnum('edge', 8)));
|
||||
return;
|
||||
}
|
||||
const alreadySet = [];
|
||||
if (top !== untouchedNode[key].top && top === bottom) {
|
||||
lines.push(setProperty(name, key, node[key].top, getEnum('edge', 7)));
|
||||
alreadySet.push('top', 'bottom');
|
||||
}
|
||||
if (left !== untouchedNode[key].left && left === right) {
|
||||
lines.push(
|
||||
setProperty(name, key, node[key].left, getEnum('edge', 6)),
|
||||
);
|
||||
alreadySet.push('left', 'right');
|
||||
}
|
||||
['left', 'top', 'right', 'bottom'].forEach((pKey, i) => {
|
||||
if (
|
||||
node[key][pKey] !== untouchedNode[key][pKey] &&
|
||||
alreadySet.indexOf(pKey) === -1
|
||||
) {
|
||||
lines.push(
|
||||
setProperty(name, key, node[key][pKey], getEnum('edge', i)),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
lines.push(setProperty(name, key, node[key]));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (node.children && node.children.size > 0) {
|
||||
lines.push(
|
||||
'',
|
||||
'// insert children',
|
||||
...node.children.map(
|
||||
(_, i) => `${name}.insertChild(${childName(i)}, ${i});`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export default function generateCode(
|
||||
root: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
): string {
|
||||
const rootNodeName = 'root';
|
||||
return [
|
||||
`import yoga, {Node} from 'yoga-layout';`,
|
||||
getLayoutCode(root, rootNodeName, 0),
|
||||
'',
|
||||
`${rootNodeName}.calculateLayout(${root.width}, ${root.height}, ${getEnum(
|
||||
'direction',
|
||||
direction,
|
||||
)});`,
|
||||
`${rootNodeName}.getComputedLayout();`,
|
||||
].join('\n');
|
||||
}
|
172
website/src/components/Playground/CodeLitho.js
Normal file
172
website/src/components/Playground/CodeLitho.js
Normal file
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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 yoga from 'yoga-layout';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import {JSEnumLookup} from './CodeJavaScript';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
|
||||
function getEnum(yogaEnum: string, value: string | number): string {
|
||||
const enumLookup = {
|
||||
justifyContent: 'Justify',
|
||||
alignItems: 'Align',
|
||||
alignContent: 'Align',
|
||||
alignSelf: 'Align',
|
||||
position: 'Position',
|
||||
flexWrap: 'Wrap',
|
||||
positionType: 'PositionType',
|
||||
direction: 'Driection',
|
||||
};
|
||||
|
||||
if (!enumLookup[yogaEnum]) {
|
||||
return String(value);
|
||||
} else {
|
||||
const enumValue = Object.keys(yoga)
|
||||
.filter(key =>
|
||||
key.toLowerCase().startsWith(JSEnumLookup[yogaEnum].toLowerCase()),
|
||||
)
|
||||
.find(key => yoga[key] === value);
|
||||
|
||||
return `Yoga${enumLookup[yogaEnum]}.${
|
||||
enumValue ? enumValue.replace(/^([A-Z]+)_/, '') : value
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
function getLayoutCode(
|
||||
node: LayoutRecordT,
|
||||
indent: string = '',
|
||||
isReturning?: boolean,
|
||||
): string {
|
||||
const lines = [];
|
||||
const flexDirection = {
|
||||
[yoga.FLEX_DIRECTION_ROW]: 'Row',
|
||||
[yoga.FLEX_DIRECTION_ROW_REVERSE]: 'RowReverse',
|
||||
[yoga.FLEX_DIRECTION_COLUMN]: 'Column',
|
||||
[yoga.FLEX_DIRECTION_COLUMN_REVERSE]: 'ColumnReverse',
|
||||
};
|
||||
|
||||
lines.push(
|
||||
indent +
|
||||
`${isReturning ? 'return ' : ''}${
|
||||
flexDirection[node.flexDirection]
|
||||
}.create(c)`,
|
||||
);
|
||||
if (node.children.size > 0) {
|
||||
lines.push(
|
||||
...node.children
|
||||
.toJSON()
|
||||
.map(
|
||||
child =>
|
||||
`${indent}\t.child(\n${getLayoutCode(child, indent + '\t\t')})`,
|
||||
),
|
||||
);
|
||||
}
|
||||
const untouchedLayout = LayoutRecord({width: '', height: ''});
|
||||
const untouchedPosition = PositionRecord({});
|
||||
Object.keys(node.toJSON()).forEach(key => {
|
||||
if (
|
||||
node[key] instanceof PositionRecord &&
|
||||
!node[key].equals(untouchedPosition)
|
||||
) {
|
||||
if (key === 'border') {
|
||||
lines.push(indent + '\t.border(', indent + '\t\tBorder.create(c)');
|
||||
}
|
||||
|
||||
const {top, left, right, bottom} = node[key].toJS();
|
||||
if (
|
||||
top !== untouchedPosition.top &&
|
||||
top === left &&
|
||||
top === right &&
|
||||
top === bottom
|
||||
) {
|
||||
// all edges
|
||||
lines.push(
|
||||
indent +
|
||||
(key === 'border'
|
||||
? `\t\t\t.widthDip(YogaEdge.ALL, ${node[key].top})`
|
||||
: `\t.${key}Dip(YogaEdge.ALL, ${node[key].top})`),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const alreadySet = [];
|
||||
if (top !== untouchedPosition.top && top === bottom) {
|
||||
lines.push(
|
||||
indent +
|
||||
(key === 'border'
|
||||
? `\t\t\t.widthDip(YogaEdge.VERTICAL, ${node[key].top})`
|
||||
: `\t.${key}Dip(YogaEdge.VERTICAL, ${node[key].top})`),
|
||||
);
|
||||
alreadySet.push('top', 'bottom');
|
||||
}
|
||||
if (left !== untouchedPosition.left && left === right) {
|
||||
lines.push(
|
||||
indent +
|
||||
(key === 'border'
|
||||
? `\t\t\t.widthDip(YogaEdge.HORIZONTAL, ${node[key].left})`
|
||||
: `\t.${key}Dip(YogaEdge.HORIZONTAL, ${node[key].left})`),
|
||||
);
|
||||
alreadySet.push('left', 'right');
|
||||
}
|
||||
['left', 'top', 'right', 'bottom'].forEach((pKey, i) => {
|
||||
if (
|
||||
node[key][pKey] !== untouchedPosition[pKey] &&
|
||||
alreadySet.indexOf(pKey) === -1
|
||||
) {
|
||||
lines.push(
|
||||
indent +
|
||||
(key === 'border'
|
||||
? `\t\t\t.widthDip(YogaEdge.${pKey.toUpperCase()}, ${
|
||||
node.border[pKey]
|
||||
})`
|
||||
: `\t.${key}Dip(YogaEdge.${pKey.toUpperCase()}, ${
|
||||
node[key][pKey]
|
||||
})`),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (key === 'border') {
|
||||
lines.push(
|
||||
indent + '\t\t\t.color(YogaEdge.ALL, 0xfff36b7f)',
|
||||
indent + '\t\t\t.build())',
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
key !== 'children' &&
|
||||
key !== 'flexDirection' &&
|
||||
node[key] !== untouchedLayout[key]
|
||||
) {
|
||||
lines.push(indent + `\t.${key}(${getEnum(key, node[key])})`);
|
||||
}
|
||||
});
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export default function generateCode(
|
||||
root: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
): string {
|
||||
return [
|
||||
'@LayoutSpec',
|
||||
'public class PlaygroundComponentSpec {',
|
||||
'\t@OnCreateLayout',
|
||||
'\tstatic Component onCreateLayout(ComponentContext c) {',
|
||||
getLayoutCode(root, '\t\t', true),
|
||||
'\t\t\t.build();',
|
||||
'\t}',
|
||||
'}',
|
||||
].join('\n');
|
||||
}
|
133
website/src/components/Playground/CodeReactNative.js
Normal file
133
website/src/components/Playground/CodeReactNative.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* 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 yoga from 'yoga-layout';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import {JSEnumLookup} from './CodeJavaScript';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
|
||||
function getEnum(yogaEnum: string, value: string | number): string {
|
||||
const enumValue = Object.keys(yoga)
|
||||
.filter(
|
||||
key =>
|
||||
JSEnumLookup[yogaEnum] &&
|
||||
key.toLowerCase().startsWith(JSEnumLookup[yogaEnum].toLowerCase()),
|
||||
)
|
||||
.find(key => yoga[key] === value);
|
||||
|
||||
return enumValue
|
||||
? "'" +
|
||||
enumValue
|
||||
.replace(/^([A-Z]+)_/, '')
|
||||
.replace('_', '-')
|
||||
.toLowerCase() +
|
||||
"'"
|
||||
: String(value);
|
||||
}
|
||||
|
||||
function getLayoutCode(node: LayoutRecordT, indent: string = ''): string {
|
||||
const lines = [];
|
||||
const untouchedLayout = LayoutRecord({width: '', height: ''});
|
||||
const untouchedPosition = PositionRecord({});
|
||||
lines.push(indent + '<View style={{');
|
||||
lines.push(indent + ' flex: 1,');
|
||||
Object.keys(node.toJSON()).forEach(key => {
|
||||
if (key === 'border' && !node.border.equals(untouchedPosition)) {
|
||||
['Top', 'Left', 'Right', 'Bottom'].forEach(pKey => {
|
||||
if (
|
||||
untouchedPosition[pKey.toLowerCase()] !==
|
||||
node.border[pKey.toLowerCase()]
|
||||
) {
|
||||
lines.push(
|
||||
indent +
|
||||
` border${pKey}Width: ${node.border[pKey.toLowerCase()]},`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (
|
||||
node[key] instanceof PositionRecord &&
|
||||
!node[key].equals(untouchedPosition)
|
||||
) {
|
||||
const {top, left, right, bottom} = node[key].toJS();
|
||||
if (
|
||||
top !== untouchedPosition.top &&
|
||||
top === left &&
|
||||
top === right &&
|
||||
top === bottom
|
||||
) {
|
||||
// all edges
|
||||
lines.push(indent + ` ${key}: ${node[key].top},`);
|
||||
return;
|
||||
}
|
||||
const alreadySet = [];
|
||||
if (top !== untouchedPosition.top && top === bottom) {
|
||||
lines.push(indent + ` ${key}Vertical: ${node[key].top},`);
|
||||
alreadySet.push('top', 'bottom');
|
||||
}
|
||||
if (left !== untouchedPosition.left && left === right) {
|
||||
lines.push(indent + ` ${key}Horizontal: ${node[key].left},`);
|
||||
alreadySet.push('left', 'right');
|
||||
}
|
||||
['left', 'top', 'right', 'bottom'].forEach((pKey, i) => {
|
||||
if (
|
||||
node[key][pKey] !== untouchedPosition[pKey] &&
|
||||
alreadySet.indexOf(pKey) === -1
|
||||
) {
|
||||
lines.push(
|
||||
indent +
|
||||
` ${key}${pKey[0].toUpperCase()}${pKey.substr(1)}: ${
|
||||
node[key][pKey]
|
||||
},`,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (key !== 'children' && node[key] !== untouchedLayout[key]) {
|
||||
lines.push(indent + ` ${key}: ${getEnum(key, node[key])},`);
|
||||
}
|
||||
});
|
||||
if (node.children.size > 0) {
|
||||
lines.push(indent + '}}>');
|
||||
} else {
|
||||
lines.push(indent + '}} />');
|
||||
}
|
||||
if (node.children.size > 0) {
|
||||
lines.push(
|
||||
...node.children
|
||||
.toJSON()
|
||||
.map(child => getLayoutCode(child, indent + ' ')),
|
||||
);
|
||||
}
|
||||
if (node.children.size > 0) {
|
||||
lines.push(indent + '</View>');
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
export default function generateCode(
|
||||
root: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
): string {
|
||||
return [
|
||||
`import React, {Component} from 'react';`,
|
||||
`import {View} from 'react-native';`,
|
||||
'',
|
||||
'export default class MyLayout extends Component {',
|
||||
' render() {',
|
||||
' return (',
|
||||
getLayoutCode(root, ' '),
|
||||
' );',
|
||||
' }',
|
||||
'};',
|
||||
].join('\n');
|
||||
}
|
49
website/src/components/Playground/Editor.css
Normal file
49
website/src/components/Playground/Editor.css
Normal file
@@ -0,0 +1,49 @@
|
||||
.Editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Editor .field {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.Editor .ant-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Editor h2 {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.Editor h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.Editor .ant-tabs-nav .ant-tabs-tab {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.Editor .EditorTabs {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Editor .ant-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.Editor .ant-tabs-bar {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.Editor .ant-tabs-tabpane {
|
||||
overflow-y: scroll;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.Editor .EditorButtons {
|
||||
padding: 15px;
|
||||
}
|
284
website/src/components/Playground/Editor.js
Normal file
284
website/src/components/Playground/Editor.js
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 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 {Row, Col, Button, Tabs, Input} from 'antd';
|
||||
import YogaEnumSelect from './YogaEnumSelect';
|
||||
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;
|
||||
|
||||
type Props = {
|
||||
node: ?LayoutRecordT,
|
||||
onChangeLayout: (key: string, value: any) => void,
|
||||
onChangeSetting: (key: string, value: any) => void,
|
||||
direction: Yoga$Direction,
|
||||
selectedNodeIsRoot: boolean,
|
||||
onRemove?: () => void,
|
||||
onAdd?: () => void,
|
||||
};
|
||||
|
||||
export default class Editor extends Component<Props> {
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
onKeyDown = (e: KeyboardEvent) => {
|
||||
if (
|
||||
(e.key === 'Delete' || e.key === 'Backspace') &&
|
||||
this.props.onRemove &&
|
||||
!(e.target instanceof HTMLInputElement)
|
||||
) {
|
||||
this.props.onRemove();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {node, selectedNodeIsRoot} = this.props;
|
||||
const disabled = !Boolean(node);
|
||||
|
||||
return (
|
||||
<div className="Editor">
|
||||
<Tabs defaultActiveKey="1" className="EditorTabs">
|
||||
<TabPane tab="Flex" key="1">
|
||||
<h2>
|
||||
Direction
|
||||
<InfoText>
|
||||
The direction property specifies the text direction/writing
|
||||
direction
|
||||
</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
property="DIRECTION"
|
||||
value={this.props.direction}
|
||||
onChange={e => this.props.onChangeSetting('direction', e)}
|
||||
/>
|
||||
<h2>
|
||||
Flex direction
|
||||
<InfoText>Defining the direction of the main-axis</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="FLEX_DIRECTION"
|
||||
value={node ? node.flexDirection : ''}
|
||||
onChange={e => this.props.onChangeLayout('flexDirection', e)}
|
||||
/>
|
||||
|
||||
<Row gutter={15} style={{marginTop: 30}}>
|
||||
<Col span={12}>
|
||||
<h2>
|
||||
Flex grow
|
||||
<InfoText>
|
||||
Grow factor defined how much space this element should take
|
||||
up, relative to it's siblings
|
||||
</InfoText>
|
||||
</h2>
|
||||
<Input
|
||||
type="text"
|
||||
disabled={disabled || selectedNodeIsRoot}
|
||||
value={node ? node.flexGrow : ''}
|
||||
onChange={e =>
|
||||
this.props.onChangeLayout('flexGrow', e.target.value)
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<h2>
|
||||
Flex shrink
|
||||
<InfoText>
|
||||
Shrink factor if elements don't fit into the parent node
|
||||
anymore.
|
||||
</InfoText>
|
||||
</h2>
|
||||
<Input
|
||||
type="text"
|
||||
disabled={disabled || selectedNodeIsRoot}
|
||||
value={node ? node.flexShrink : ''}
|
||||
onChange={e =>
|
||||
this.props.onChangeLayout('flexShrink', e.target.value)
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<h2>
|
||||
Flex wrap
|
||||
<InfoText>
|
||||
Wrapping behaviour when child nodes don't fit into a single line
|
||||
</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="WRAP"
|
||||
value={node ? node.flexWrap : ''}
|
||||
onChange={e => this.props.onChangeLayout('flexWrap', e)}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab="Alignment" key="2">
|
||||
<h2>
|
||||
Justify content
|
||||
<InfoText>Aligns child nodes along the main-axis</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="JUSTIFY"
|
||||
value={node ? node.justifyContent : ''}
|
||||
onChange={e => this.props.onChangeLayout('justifyContent', e)}
|
||||
/>
|
||||
|
||||
<h2>
|
||||
Align items
|
||||
<InfoText>Aligns child nodes along the cross-axis</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="ALIGN"
|
||||
value={node ? node.alignItems : ''}
|
||||
onChange={e => this.props.onChangeLayout('alignItems', e)}
|
||||
/>
|
||||
|
||||
<h2>
|
||||
Align self
|
||||
<InfoText>
|
||||
Specifies alignment on the cross-axis for the node itself
|
||||
</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled || selectedNodeIsRoot}
|
||||
property="ALIGN"
|
||||
value={node ? node.alignSelf : ''}
|
||||
onChange={e => this.props.onChangeLayout('alignSelf', e)}
|
||||
/>
|
||||
|
||||
<h2>
|
||||
Align content
|
||||
<InfoText>
|
||||
Alignment of lines along the cross-axis when wrapping
|
||||
</InfoText>
|
||||
</h2>
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="ALIGN"
|
||||
value={node ? node.alignContent : ''}
|
||||
onChange={e => this.props.onChangeLayout('alignContent', e)}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab="Layout" key="3">
|
||||
<h2>
|
||||
Width × height
|
||||
<InfoText>Dimensions of the node</InfoText>
|
||||
</h2>
|
||||
<Row gutter={15}>
|
||||
<Col span={12}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="width"
|
||||
disabled={disabled}
|
||||
value={node ? node.width : ''}
|
||||
onChange={e =>
|
||||
this.props.onChangeLayout('width', e.target.value)
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="height"
|
||||
disabled={disabled}
|
||||
value={node ? node.height : ''}
|
||||
onChange={e =>
|
||||
this.props.onChangeLayout('height', e.target.value)
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<h2>
|
||||
Aspect ratio
|
||||
<InfoText>
|
||||
Aspect radio is an additon by Yoga which is handy e.g. for nodes
|
||||
displaying videos
|
||||
</InfoText>
|
||||
</h2>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Aspect ratio"
|
||||
disabled={disabled}
|
||||
value={node ? node.aspectRatio : ''}
|
||||
onChange={e =>
|
||||
this.props.onChangeLayout('aspectRatio', e.target.value)
|
||||
}
|
||||
/>
|
||||
|
||||
<h2>Box model</h2>
|
||||
{['padding', 'border', 'margin'].map(property => (
|
||||
<YogaPositionEditor
|
||||
property={property}
|
||||
key={property}
|
||||
value={node ? node[property] : undefined}
|
||||
onChange={value => this.props.onChangeLayout(property, value)}
|
||||
/>
|
||||
))}
|
||||
<h2>
|
||||
Position
|
||||
<InfoText>
|
||||
Relative position offsets the node from it's calculated
|
||||
position. Absolute position removes the node from the flexbox
|
||||
flow and positions it at the given position.
|
||||
</InfoText>
|
||||
</h2>
|
||||
|
||||
<YogaEnumSelect
|
||||
disabled={disabled}
|
||||
property="POSITION_TYPE"
|
||||
value={node ? node.positionType : ''}
|
||||
onChange={e => this.props.onChangeLayout('positionType', e)}
|
||||
/>
|
||||
<YogaPositionEditor
|
||||
property="position"
|
||||
value={node ? node.position : undefined}
|
||||
onChange={value => this.props.onChangeLayout('position', value)}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Row gutter={15} className="EditorButtons">
|
||||
<Col span={12}>
|
||||
<Button
|
||||
icon="plus-circle-o"
|
||||
disabled={!Boolean(this.props.onAdd)}
|
||||
onClick={this.props.onAdd}
|
||||
type="primary">
|
||||
add child node
|
||||
</Button>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Button
|
||||
icon="close-circle-o"
|
||||
disabled={!Boolean(this.props.onRemove)}
|
||||
onClick={this.props.onRemove}
|
||||
type="danger">
|
||||
remove node
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
9
website/src/components/Playground/InfoText.css
Normal file
9
website/src/components/Playground/InfoText.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.InfoText {
|
||||
max-width: 230px;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
.InfoTextIcon {
|
||||
margin-left: 5px;
|
||||
opacity: 0.5;
|
||||
}
|
31
website/src/components/Playground/InfoText.js
Normal file
31
website/src/components/Playground/InfoText.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 {Popover, Icon} from 'antd';
|
||||
import './InfoText.css';
|
||||
|
||||
type Props = {
|
||||
children: any,
|
||||
};
|
||||
|
||||
export default class InfoText extends Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<Popover
|
||||
content={<div className="InfoText">{this.props.children}</div>}
|
||||
trigger="hover">
|
||||
<Icon className="InfoTextIcon" type="info-circle-o" />
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
68
website/src/components/Playground/LayoutRecord.js
Normal file
68
website/src/components/Playground/LayoutRecord.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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 {Record, List} from 'immutable';
|
||||
import type {RecordOf} from 'immutable';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import type {PositionRecordT} from './PositionRecord';
|
||||
import yoga from 'yoga-layout';
|
||||
|
||||
import type {
|
||||
Yoga$Align,
|
||||
Yoga$JustifyContent,
|
||||
Yoga$FlexDirection,
|
||||
Yoga$FlexWrap,
|
||||
Yoga$Yoga$PositionType,
|
||||
} from 'yoga-layout';
|
||||
|
||||
export type LayoutRecordT = RecordOf<{
|
||||
width?: ?number,
|
||||
height?: ?number,
|
||||
justifyContent?: Yoga$JustifyContent,
|
||||
padding: PositionRecordT,
|
||||
border: PositionRecordT,
|
||||
margin: PositionRecordT,
|
||||
position: PositionRecordT,
|
||||
positionType: Yoga$Yoga$PositionType,
|
||||
alignItems?: Yoga$Align,
|
||||
alignSelf?: Yoga$Align,
|
||||
alignContent?: Yoga$Align,
|
||||
flexDirection?: Yoga$FlexDirection,
|
||||
flexGrow?: number,
|
||||
flexShrink?: number,
|
||||
padding?: number,
|
||||
flexWrap?: Yoga$FlexWrap,
|
||||
aspectRatio?: number,
|
||||
children?: List<LayoutDefinition>,
|
||||
}>;
|
||||
|
||||
const r: LayoutRecordT = Record({
|
||||
width: 100,
|
||||
height: 100,
|
||||
justifyContent: yoga.JUSTIFY_FLEX_START,
|
||||
alignItems: yoga.ALIGN_STRETCH,
|
||||
alignSelf: yoga.ALIGN_AUTO,
|
||||
alignContent: yoga.ALIGN_STRETCH,
|
||||
flexDirection: yoga.FLEX_DIRECTION_ROW,
|
||||
padding: PositionRecord(),
|
||||
margin: PositionRecord(),
|
||||
border: PositionRecord(),
|
||||
position: PositionRecord(),
|
||||
positionType: yoga.POSITION_TYPE_RELATIVE,
|
||||
flexWrap: yoga.WRAP_NO_WRAP,
|
||||
flexGrow: 0,
|
||||
flexShrink: 1,
|
||||
children: List(),
|
||||
aspectRatio: 'auto',
|
||||
});
|
||||
|
||||
export default r;
|
9
website/src/components/Playground/PositionGuide.css
Normal file
9
website/src/components/Playground/PositionGuide.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.PositionGuide {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
user-select: none;
|
||||
}
|
148
website/src/components/Playground/PositionGuide.js
Normal file
148
website/src/components/Playground/PositionGuide.js
Normal file
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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 PositionRecord from './PositionRecord';
|
||||
import type {PositionRecordT} from './PositionRecord';
|
||||
import './PositionGuide.css';
|
||||
|
||||
type Props = {
|
||||
inset?: boolean,
|
||||
reverse?: boolean,
|
||||
position: PositionRecordT,
|
||||
offset: PositionRecordT,
|
||||
color: string,
|
||||
};
|
||||
|
||||
export default class PositionGuide extends Component<Props> {
|
||||
static defaultProps = {
|
||||
offset: PositionRecord({}),
|
||||
};
|
||||
|
||||
render() {
|
||||
const {position, offset, inset, color, reverse} = this.props;
|
||||
let {top, left, right, bottom} = position;
|
||||
let {top: oTop, left: oLeft, right: oRight, bottom: oBottom} = offset;
|
||||
|
||||
if (reverse) {
|
||||
let temp1 = left;
|
||||
left = right;
|
||||
right = temp1;
|
||||
temp1 = oLeft;
|
||||
oLeft = oRight;
|
||||
oRight = temp1;
|
||||
}
|
||||
|
||||
if (!top) {
|
||||
top = 0;
|
||||
}
|
||||
if (!left) {
|
||||
left = 0;
|
||||
}
|
||||
if (!right) {
|
||||
right = 0;
|
||||
}
|
||||
if (!bottom) {
|
||||
bottom = 0;
|
||||
}
|
||||
if (!oTop) {
|
||||
oTop = 0;
|
||||
}
|
||||
if (!oLeft) {
|
||||
oLeft = 0;
|
||||
}
|
||||
if (!oRight) {
|
||||
oRight = 0;
|
||||
}
|
||||
if (!oBottom) {
|
||||
oBottom = 0;
|
||||
}
|
||||
|
||||
if (!inset) {
|
||||
if (top < 0) {
|
||||
bottom -= top;
|
||||
top = 0;
|
||||
}
|
||||
if (bottom < 0) {
|
||||
top -= bottom;
|
||||
bottom = 0;
|
||||
}
|
||||
if (left < 0) {
|
||||
right -= left;
|
||||
left = 0;
|
||||
}
|
||||
if (right < 0) {
|
||||
left -= right;
|
||||
right = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
top !== 0 ? (
|
||||
<div
|
||||
key="top"
|
||||
className="PositionGuide"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
height: top,
|
||||
top: inset ? oTop : -top - oTop,
|
||||
left: inset ? left + oLeft : -left - oLeft,
|
||||
right: inset ? right + oRight : -right - oRight,
|
||||
}}>
|
||||
{top}
|
||||
</div>
|
||||
) : null,
|
||||
left !== 0 ? (
|
||||
<div
|
||||
key="left"
|
||||
className="PositionGuide"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
width: left,
|
||||
top: inset ? oTop : -oTop,
|
||||
bottom: inset ? oBottom : -oBottom,
|
||||
left: inset ? oLeft : -left - oLeft,
|
||||
}}>
|
||||
{left}
|
||||
</div>
|
||||
) : null,
|
||||
right !== 0 ? (
|
||||
<div
|
||||
key="right"
|
||||
className="PositionGuide"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
width: right,
|
||||
top: inset ? oTop : -oTop,
|
||||
bottom: inset ? oBottom : -oBottom,
|
||||
right: inset ? oRight : -right - oRight,
|
||||
}}>
|
||||
{right}
|
||||
</div>
|
||||
) : null,
|
||||
bottom !== 0 ? (
|
||||
<div
|
||||
key="bottom"
|
||||
className="PositionGuide"
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
height: bottom,
|
||||
bottom: inset ? oBottom : -bottom - oBottom,
|
||||
left: inset ? left + oLeft : -left - oLeft,
|
||||
right: inset ? right + oRight : -right - oRight,
|
||||
}}>
|
||||
{bottom}
|
||||
</div>
|
||||
) : null,
|
||||
];
|
||||
}
|
||||
}
|
30
website/src/components/Playground/PositionRecord.js
Normal file
30
website/src/components/Playground/PositionRecord.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 {Record} from 'immutable';
|
||||
import type {RecordOf} from 'immutable';
|
||||
|
||||
export type PositionRecordT = RecordOf<{
|
||||
top: string,
|
||||
right: string,
|
||||
bottom: string,
|
||||
left: string,
|
||||
}>;
|
||||
|
||||
const r: PositionRecordT = Record({
|
||||
top: '',
|
||||
right: '',
|
||||
bottom: '',
|
||||
left: '',
|
||||
});
|
||||
|
||||
export default r;
|
52
website/src/components/Playground/Sidebar.css
Normal file
52
website/src/components/Playground/Sidebar.css
Normal file
@@ -0,0 +1,52 @@
|
||||
.Sidebar {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 350px;
|
||||
background: white;
|
||||
border-left: 1px solid #dddfe2;
|
||||
transform: translateX(100%);
|
||||
transition: 0.2s transform;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.Sidebar.floating {
|
||||
margin: 25px;
|
||||
max-height: calc(100% - 50px);
|
||||
border-radius: 6px;
|
||||
bottom: auto;
|
||||
box-shadow: 3px 3px 15px rgba(0, 0, 0, 0.15);
|
||||
opacity: 0;
|
||||
transform: translateY(15px);
|
||||
transition: 0.2s all;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.Sidebar.visible {
|
||||
transform: translateX(0);
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.Sidebar.floating.visible {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.SidebarClose {
|
||||
text-align: right;
|
||||
padding: 15px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.SidebarClose i {
|
||||
cursor: pointer;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.SidebarClose i:hover {
|
||||
opacity: 0.6;
|
||||
}
|
41
website/src/components/Playground/Sidebar.js
Normal file
41
website/src/components/Playground/Sidebar.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 {Icon} from 'antd';
|
||||
import './Sidebar.css';
|
||||
|
||||
type Props = {
|
||||
onClose?: () => void,
|
||||
width?: number,
|
||||
children: any,
|
||||
floating?: boolean,
|
||||
};
|
||||
|
||||
export default class Sidebar extends Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className={`Sidebar ${this.props.visible ? 'visible' : ''} ${
|
||||
this.props.floating ? 'floating' : ''
|
||||
}`}
|
||||
style={{width: this.props.width}}>
|
||||
{this.props.onClose && (
|
||||
<div className="SidebarClose">
|
||||
<Icon type="close" onClick={this.props.onClose} />
|
||||
</div>
|
||||
)}
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
9
website/src/components/Playground/YogaEnumSelect.css
Normal file
9
website/src/components/Playground/YogaEnumSelect.css
Normal file
@@ -0,0 +1,9 @@
|
||||
.YogaEnumSelect.ant-radio-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.YogaEnumSelect.ant-radio-group .ant-radio-button-wrapper {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
text-align: center;
|
||||
}
|
82
website/src/components/Playground/YogaEnumSelect.js
Normal file
82
website/src/components/Playground/YogaEnumSelect.js
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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 yoga from 'yoga-layout';
|
||||
import {Radio, Menu, Dropdown, Button, Icon} from 'antd';
|
||||
import './YogaEnumSelect.css';
|
||||
const RadioButton = Radio.Button;
|
||||
const RadioGroup = Radio.Group;
|
||||
|
||||
type Props = {
|
||||
property: string,
|
||||
disabled?: boolean,
|
||||
value: string | number,
|
||||
onChange: (value: number) => void,
|
||||
};
|
||||
|
||||
export default class YogaEnumSelect extends Component<Props> {
|
||||
values: Array<{key: string, value: number}>;
|
||||
|
||||
constructor({property}: Props) {
|
||||
super();
|
||||
// $FlowFixMe
|
||||
this.values = Object.keys(yoga)
|
||||
.map(key => ({key, value: yoga[key]}))
|
||||
.filter(
|
||||
({key}) => key.startsWith(property) && key !== `${property}_COUNT`,
|
||||
);
|
||||
}
|
||||
|
||||
handleMenuClick = ({key}: {key: string}) => {
|
||||
this.props.onChange(yoga[key]);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {property, ...props} = this.props;
|
||||
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>
|
||||
) : (
|
||||
<RadioGroup
|
||||
{...props}
|
||||
onChange={e => this.props.onChange(e.target.value)}
|
||||
defaultValue="a"
|
||||
className="YogaEnumSelect">
|
||||
{this.values.map(({key, value}) => (
|
||||
<RadioButton key={key} value={value}>
|
||||
{key.replace(new RegExp(`^${property}_`), '')}
|
||||
</RadioButton>
|
||||
))}
|
||||
</RadioGroup>
|
||||
);
|
||||
}
|
||||
}
|
51
website/src/components/Playground/YogaNode.css
Normal file
51
website/src/components/Playground/YogaNode.css
Normal file
@@ -0,0 +1,51 @@
|
||||
.YogaNode {
|
||||
background: white;
|
||||
position: absolute;
|
||||
transform: scale(1);
|
||||
box-shadow: inset 0 0 0px 1px rgba(48, 56, 69, 0.2);
|
||||
transition: 0.2s all, 0s outline, 0s box-shadow;
|
||||
}
|
||||
|
||||
.YogaNode .YogaNode {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.YogaNode > .info {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.YogaNode:hover > .info {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.YogaNode:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.YogaNode.focused {
|
||||
box-shadow: 0 0 0 2px #95ddcf, 0 0 15px rgba(0, 0, 0, 0.2),
|
||||
inset 0 0 0px 1px rgba(255, 255, 255, 0.2);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.YogaNode.invisible {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
.YogaNode .label {
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
letter-spacing: 1px;
|
||||
}
|
283
website/src/components/Playground/YogaNode.js
Normal file
283
website/src/components/Playground/YogaNode.js
Normal file
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* 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 yoga, {Node} from 'yoga-layout';
|
||||
import PositionGuide from './PositionGuide';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
|
||||
import './YogaNode.css';
|
||||
|
||||
type Yoga$Node = any;
|
||||
|
||||
type ComputedLayout = {|
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
children: Array<ComputedLayout>,
|
||||
node: Yoga$Node,
|
||||
|};
|
||||
|
||||
type Props = {|
|
||||
layoutDefinition: LayoutRecordT,
|
||||
className?: string,
|
||||
computedLayout?: ComputedLayout,
|
||||
path: Array<number>,
|
||||
selectedNodePath?: ?Array<number>,
|
||||
direction?: Yoga$Direction,
|
||||
label?: string,
|
||||
showGuides: boolean,
|
||||
onClick?: (path: Array<number>) => void,
|
||||
onDoubleClick?: (path: Array<number>) => void,
|
||||
|};
|
||||
|
||||
type State = {
|
||||
visible?: boolean,
|
||||
};
|
||||
|
||||
export default class YogaNode extends Component<Props, State> {
|
||||
node: Yoga$Node;
|
||||
|
||||
static defaultProps = {
|
||||
path: [],
|
||||
label: 'root',
|
||||
showGuides: true,
|
||||
};
|
||||
|
||||
state = {};
|
||||
computedLayout: ?ComputedLayout;
|
||||
rootNode: ?Yoga$Node;
|
||||
|
||||
constructor(props: Props) {
|
||||
super();
|
||||
if (!props.computedLayout) {
|
||||
// is root node
|
||||
this.calculateLayout(props);
|
||||
this.state = {
|
||||
visible: !Boolean(props.computedLayout),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setTimeout(() => this.setState({visible: true}), 200);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
if (
|
||||
!nextProps.computedLayout &&
|
||||
(!this.props.layoutDefinition.equals(nextProps.layoutDefinition) ||
|
||||
this.props.direction !== nextProps.direction)
|
||||
) {
|
||||
// is root node and the layout definition or settings changed
|
||||
this.calculateLayout(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.rootNode) {
|
||||
this.rootNode.freeRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
calculateLayout(props: Props) {
|
||||
const root = this.createYogaNodes(props.layoutDefinition);
|
||||
root.calculateLayout(
|
||||
props.layoutDefinition.width,
|
||||
props.layoutDefinition.height,
|
||||
props.direction,
|
||||
);
|
||||
this.computedLayout = this.getComputedLayout(root);
|
||||
this.rootNode = root;
|
||||
}
|
||||
|
||||
createYogaNodes = (layoutDefinition: LayoutRecordT): Yoga$Node => {
|
||||
const root = Node.create();
|
||||
root.setWidth(layoutDefinition.width);
|
||||
root.setHeight(layoutDefinition.height);
|
||||
root.setJustifyContent(layoutDefinition.justifyContent);
|
||||
root.setAlignItems(layoutDefinition.alignItems);
|
||||
root.setAlignSelf(layoutDefinition.alignSelf);
|
||||
root.setFlexGrow(layoutDefinition.flexGrow);
|
||||
root.setFlexShrink(layoutDefinition.flexShrink);
|
||||
|
||||
root.setPadding(yoga.EDGE_TOP, layoutDefinition.padding.top);
|
||||
root.setPadding(yoga.EDGE_RIGHT, layoutDefinition.padding.right);
|
||||
root.setPadding(yoga.EDGE_BOTTOM, layoutDefinition.padding.bottom);
|
||||
root.setPadding(yoga.EDGE_LEFT, layoutDefinition.padding.left);
|
||||
|
||||
root.setBorder(yoga.EDGE_TOP, layoutDefinition.border.top);
|
||||
root.setBorder(yoga.EDGE_RIGHT, layoutDefinition.border.right);
|
||||
root.setBorder(yoga.EDGE_BOTTOM, layoutDefinition.border.bottom);
|
||||
root.setBorder(yoga.EDGE_LEFT, layoutDefinition.border.left);
|
||||
|
||||
root.setMargin(yoga.EDGE_TOP, layoutDefinition.margin.top);
|
||||
root.setMargin(yoga.EDGE_RIGHT, layoutDefinition.margin.right);
|
||||
root.setMargin(yoga.EDGE_BOTTOM, layoutDefinition.margin.bottom);
|
||||
root.setMargin(yoga.EDGE_LEFT, layoutDefinition.margin.left);
|
||||
|
||||
root.setPosition(yoga.EDGE_TOP, layoutDefinition.position.top);
|
||||
root.setPosition(yoga.EDGE_RIGHT, layoutDefinition.position.right);
|
||||
root.setPosition(yoga.EDGE_BOTTOM, layoutDefinition.position.bottom);
|
||||
root.setPosition(yoga.EDGE_LEFT, layoutDefinition.position.left);
|
||||
|
||||
root.setPositionType(layoutDefinition.positionType);
|
||||
|
||||
root.setDisplay(yoga.DISPLAY_FLEX);
|
||||
root.setAspectRatio(layoutDefinition.aspectRatio);
|
||||
root.setFlexWrap(layoutDefinition.flexWrap);
|
||||
root.setFlexDirection(layoutDefinition.flexDirection);
|
||||
(layoutDefinition.children || [])
|
||||
.map(this.createYogaNodes)
|
||||
.forEach((node, i) => {
|
||||
root.insertChild(node, i);
|
||||
});
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
getComputedLayout = (node: Yoga$Node): ComputedLayout => {
|
||||
return {
|
||||
...node.getComputedLayout(),
|
||||
node,
|
||||
children: Array.apply(null, Array(node.getChildCount())).map((_, i) =>
|
||||
this.getComputedLayout(node.getChild(i)),
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
onClick = (e: SyntheticMouseEvent<>) => {
|
||||
const {onClick} = this.props;
|
||||
if (onClick) {
|
||||
e.stopPropagation();
|
||||
onClick(this.props.path);
|
||||
}
|
||||
};
|
||||
|
||||
onDoubleClick = (e: SyntheticMouseEvent<>) => {
|
||||
const {onDoubleClick} = this.props;
|
||||
if (onDoubleClick) {
|
||||
e.stopPropagation();
|
||||
onDoubleClick(this.props.path);
|
||||
}
|
||||
};
|
||||
|
||||
showPositionGuides({node}: ComputedLayout) {
|
||||
const padding = PositionRecord({
|
||||
top: node.getComputedPadding(yoga.EDGE_TOP),
|
||||
left: node.getComputedPadding(yoga.EDGE_LEFT),
|
||||
right: node.getComputedPadding(yoga.EDGE_RIGHT),
|
||||
bottom: node.getComputedPadding(yoga.EDGE_BOTTOM),
|
||||
});
|
||||
const border = PositionRecord({
|
||||
top: node.getComputedBorder(yoga.EDGE_TOP),
|
||||
left: node.getComputedBorder(yoga.EDGE_LEFT),
|
||||
right: node.getComputedBorder(yoga.EDGE_RIGHT),
|
||||
bottom: node.getComputedBorder(yoga.EDGE_BOTTOM),
|
||||
});
|
||||
const margin = PositionRecord({
|
||||
top: node.getComputedMargin(yoga.EDGE_TOP),
|
||||
left: node.getComputedMargin(yoga.EDGE_LEFT),
|
||||
right: node.getComputedMargin(yoga.EDGE_RIGHT),
|
||||
bottom: node.getComputedMargin(yoga.EDGE_BOTTOM),
|
||||
});
|
||||
const position = PositionRecord({
|
||||
top: node.getPosition(yoga.EDGE_TOP).value,
|
||||
left: node.getPosition(yoga.EDGE_LEFT).value,
|
||||
right: node.getPosition(yoga.EDGE_RIGHT).value,
|
||||
bottom: node.getPosition(yoga.EDGE_BOTTOM).value,
|
||||
});
|
||||
|
||||
return [
|
||||
<PositionGuide
|
||||
key="border"
|
||||
inset
|
||||
position={border}
|
||||
color="rgba(251, 170, 51, 0.15)"
|
||||
reverse={node.getFlexWrap() === yoga.WRAP_WRAP_REVERSE}
|
||||
/>,
|
||||
<PositionGuide
|
||||
key="padding"
|
||||
inset
|
||||
offset={border}
|
||||
position={padding}
|
||||
color="rgba(123, 179, 41, 0.1)"
|
||||
reverse={node.getFlexWrap() === yoga.WRAP_WRAP_REVERSE}
|
||||
/>,
|
||||
<PositionGuide
|
||||
key="margin"
|
||||
position={margin}
|
||||
color="rgba(214, 43, 28, 0.1)"
|
||||
/>,
|
||||
<PositionGuide
|
||||
key="position"
|
||||
offset={margin}
|
||||
position={position}
|
||||
color="rgba(115, 51, 205, 0.1)"
|
||||
/>,
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
layoutDefinition,
|
||||
className,
|
||||
path,
|
||||
selectedNodePath,
|
||||
label,
|
||||
} = this.props;
|
||||
|
||||
// $FlowFixMe
|
||||
const computedLayout: ComputedLayout =
|
||||
this.props.computedLayout || this.computedLayout;
|
||||
const {left, top, width, height, children} = computedLayout;
|
||||
|
||||
const isFocused = selectedNodePath && selectedNodePath.length === 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`YogaNode ${isFocused ? 'focused' : ''} ${className || ''} ${
|
||||
this.state.visible ? '' : 'invisible'
|
||||
}`}
|
||||
style={{left, top, width, height}}
|
||||
onDoubleClick={this.onDoubleClick}
|
||||
onClick={this.onClick}>
|
||||
{label && <div className="label">{label}</div>}
|
||||
{isFocused &&
|
||||
this.props.showGuides &&
|
||||
this.showPositionGuides(computedLayout)}
|
||||
{(children || []).map((child: ComputedLayout, i) => (
|
||||
<YogaNode
|
||||
key={i}
|
||||
computedLayout={child}
|
||||
label={String(i + 1)}
|
||||
layoutDefinition={layoutDefinition.children.get(i)}
|
||||
selectedNodePath={
|
||||
selectedNodePath &&
|
||||
selectedNodePath.length > 0 &&
|
||||
selectedNodePath[0] === i
|
||||
? selectedNodePath.slice(1)
|
||||
: null
|
||||
}
|
||||
path={path.concat(i)}
|
||||
onClick={this.props.onClick}
|
||||
onDoubleClick={this.props.onDoubleClick}
|
||||
showGuides={this.props.showGuides}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
22
website/src/components/Playground/YogaPositionEditor.css
Normal file
22
website/src/components/Playground/YogaPositionEditor.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.YogaPositionEditor {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
width: 60%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.YogaPositionEditor input {
|
||||
width: 55px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.YogaPositionEditorRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
60
website/src/components/Playground/YogaPositionEditor.js
Normal file
60
website/src/components/Playground/YogaPositionEditor.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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 {Input} from 'antd';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import type {PositionRecordT} from './PositionRecord';
|
||||
import './YogaPositionEditor.css';
|
||||
|
||||
type Props = {
|
||||
value: PositionRecordT,
|
||||
property: string,
|
||||
onChange: (value: PositionRecordT) => void,
|
||||
};
|
||||
|
||||
export default class YogaPositionEditor extends Component<Props> {
|
||||
static defaultProps = {
|
||||
value: PositionRecord(),
|
||||
};
|
||||
|
||||
render() {
|
||||
const {onChange, value, property} = this.props;
|
||||
return (
|
||||
<div className="YogaPositionEditor">
|
||||
<Input
|
||||
type="text"
|
||||
value={value.top}
|
||||
onChange={e => onChange(value.set('top', e.target.value))}
|
||||
/>
|
||||
<div className="YogaPositionEditorRow">
|
||||
<Input
|
||||
type="text"
|
||||
value={value.left}
|
||||
onChange={e => onChange(value.set('left', e.target.value))}
|
||||
/>
|
||||
{property}
|
||||
<Input
|
||||
type="text"
|
||||
value={value.right}
|
||||
onChange={e => onChange(value.set('right', e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
type="text"
|
||||
value={value.bottom}
|
||||
onChange={e => onChange(value.set('bottom', e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
34
website/src/components/Playground/index.css
Normal file
34
website/src/components/Playground/index.css
Normal file
@@ -0,0 +1,34 @@
|
||||
.PlaygroundContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Playground {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(-90deg, rgba(0, 0, 0, 0.02) 1px, transparent 1px),
|
||||
linear-gradient(rgba(0, 0, 0, 0.02) 1px, transparent 1px),
|
||||
linear-gradient(-90deg, rgba(0, 0, 0, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(rgba(0, 0, 0, 0.03) 1px, transparent 1px),
|
||||
linear-gradient(
|
||||
transparent 4px,
|
||||
#f5f5f5 4px,
|
||||
#f5f5f5 97px,
|
||||
transparent 97px
|
||||
),
|
||||
linear-gradient(-90deg, #e5e5e5 1px, transparent 1px),
|
||||
linear-gradient(
|
||||
-90deg,
|
||||
transparent 4px,
|
||||
#f5f5f5 4px,
|
||||
#f5f5f5 97px,
|
||||
transparent 97px
|
||||
),
|
||||
linear-gradient(#e5e5e5 1px, transparent 1px), #f5f5f5;
|
||||
background-size: 10px 10px, 10px 10px, 100px 100px, 100px 100px, 100px 100px,
|
||||
100px 100px, 100px 100px, 100px 100px;
|
||||
}
|
320
website/src/components/Playground/index.js
Normal file
320
website/src/components/Playground/index.js
Normal file
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* 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 yoga from 'yoga-layout';
|
||||
import YogaNode from './YogaNode';
|
||||
import Editor from './Editor';
|
||||
import {List, setIn} from 'immutable';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
// import Toolbar from './Toolbar';
|
||||
import Code from './Code';
|
||||
import Sidebar from './Sidebar';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
import './index.css';
|
||||
|
||||
type Props = {
|
||||
layoutDefinition: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
maxDepth: number,
|
||||
maxChildren?: number,
|
||||
minChildren?: number,
|
||||
selectedNodePath?: Array<number>,
|
||||
showGuides: boolean,
|
||||
className?: string,
|
||||
height?: string | number,
|
||||
renderSidebar?: (layoutDefinition: LayoutRecordT, onChange: Function) => any,
|
||||
};
|
||||
|
||||
type State = {
|
||||
selectedNodePath: ?Array<number>,
|
||||
layoutDefinition: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
showCode: boolean,
|
||||
};
|
||||
|
||||
function getPath(path: Array<number>): Array<mixed> {
|
||||
return path.reduce((acc, cv) => acc.concat('children', cv), []);
|
||||
}
|
||||
|
||||
export default class Playground extends Component<Props, State> {
|
||||
_containerRef: ?HTMLElement;
|
||||
|
||||
static defaultProps = {
|
||||
layoutDefinition: LayoutRecord({
|
||||
width: 800,
|
||||
height: 400,
|
||||
justifyContent: yoga.JUSTIFY_SPACE_BETWEEN,
|
||||
alignItems: yoga.ALIGN_FLEX_START,
|
||||
children: List([LayoutRecord(), LayoutRecord()]),
|
||||
padding: PositionRecord({
|
||||
left: '10',
|
||||
top: '10',
|
||||
right: '10',
|
||||
bottom: '10',
|
||||
}),
|
||||
margin: PositionRecord({
|
||||
left: '20',
|
||||
top: '70',
|
||||
}),
|
||||
}),
|
||||
direction: yoga.DIRECTION_LTR,
|
||||
maxDepth: 3,
|
||||
showCode: false,
|
||||
showGuides: true,
|
||||
};
|
||||
|
||||
state = {
|
||||
selectedNodePath: this.props.selectedNodePath,
|
||||
layoutDefinition: this.props.layoutDefinition,
|
||||
direction: this.props.direction,
|
||||
showCode: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.onKeyDown);
|
||||
|
||||
// rehydrate
|
||||
if (window.location.hash && window.location.hash.length > 1) {
|
||||
try {
|
||||
const restoredState = JSON.parse(atob(window.location.hash.substr(1)));
|
||||
this.setState({layoutDefinition: this.rehydrate(restoredState)});
|
||||
} catch (e) {
|
||||
window.location.hash = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
rehydrate = (node: Object): LayoutRecord => {
|
||||
let record = LayoutRecord(node);
|
||||
record = record.set('padding', PositionRecord(record.padding));
|
||||
record = record.set('border', PositionRecord(record.border));
|
||||
record = record.set('margin', PositionRecord(record.margin));
|
||||
record = record.set('position', PositionRecord(record.position));
|
||||
record = record.set('children', List(record.children.map(this.rehydrate)));
|
||||
return record;
|
||||
};
|
||||
|
||||
onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.hideSidePanes();
|
||||
}
|
||||
};
|
||||
|
||||
onMouseDown = (e: MouseEvent) => {
|
||||
if (e.target === this._containerRef) {
|
||||
this.hideSidePanes();
|
||||
}
|
||||
};
|
||||
|
||||
hideSidePanes() {
|
||||
if (!Boolean(this.props.renderSidebar)) {
|
||||
// only unselect if we don't have an external sidebar, otherwise the
|
||||
// sidebar may rely on a certain node to be selected
|
||||
this.setState({
|
||||
selectedNodePath: null,
|
||||
showCode: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onChangeLayout = (key: string, value: any) => {
|
||||
const {selectedNodePath} = this.state;
|
||||
if (selectedNodePath) {
|
||||
this.modifyAtPath([...getPath(selectedNodePath), key], value);
|
||||
}
|
||||
};
|
||||
|
||||
onRemove = () => {
|
||||
const {selectedNodePath, layoutDefinition} = this.state;
|
||||
if (selectedNodePath) {
|
||||
const index = selectedNodePath.pop();
|
||||
const path = getPath(selectedNodePath).concat('children');
|
||||
const updatedChildren = layoutDefinition.getIn(path).delete(index);
|
||||
this.modifyAtPath(path, updatedChildren);
|
||||
this.setState({selectedNodePath: null});
|
||||
}
|
||||
};
|
||||
|
||||
onAdd = () => {
|
||||
const {selectedNodePath, layoutDefinition} = this.state;
|
||||
if (selectedNodePath) {
|
||||
const path = getPath(selectedNodePath).concat('children');
|
||||
const updatedChildren = layoutDefinition.getIn(path).push(LayoutRecord());
|
||||
this.modifyAtPath(
|
||||
path,
|
||||
updatedChildren,
|
||||
selectedNodePath.concat(updatedChildren.size - 1),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
modifyAtPath(
|
||||
path: Array<any>,
|
||||
value: any,
|
||||
selectedNodePath?: ?Array<number> = this.state.selectedNodePath,
|
||||
) {
|
||||
console.log(setIn);
|
||||
// $FlowFixMe
|
||||
const layoutDefinition = setIn(this.state.layoutDefinition, path, value);
|
||||
this.setState({
|
||||
layoutDefinition,
|
||||
selectedNodePath,
|
||||
});
|
||||
|
||||
window.location.hash = btoa(
|
||||
JSON.stringify(this.removeUnchangedProperties(layoutDefinition)),
|
||||
);
|
||||
}
|
||||
|
||||
removeUnchangedProperties = (node: LayoutRecordT): Object => {
|
||||
const untouchedLayout = LayoutRecord({});
|
||||
const untouchedPosition = PositionRecord({});
|
||||
const result = {};
|
||||
if (!node.equals(untouchedLayout)) {
|
||||
Object.keys(node.toJS()).forEach(key => {
|
||||
if (key === 'children' && node.children.size > 0) {
|
||||
result.children = node.children
|
||||
.toJSON()
|
||||
.map(this.removeUnchangedProperties);
|
||||
} else if (
|
||||
node[key] instanceof PositionRecord &&
|
||||
!node[key].equals(untouchedPosition)
|
||||
) {
|
||||
result[key] = {};
|
||||
Object.keys(untouchedPosition.toJS()).forEach(position => {
|
||||
if (node[key][position] !== untouchedPosition[position]) {
|
||||
result[key][position] = node[key][position];
|
||||
}
|
||||
});
|
||||
} else if (node[key] !== untouchedLayout[key]) {
|
||||
result[key] = node[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
getChildrenCountForSelectedPath = (): number => {
|
||||
const selectedNode: ?LayoutRecordT = (
|
||||
this.state.selectedNodePath || []
|
||||
).reduce(
|
||||
(node: LayoutRecordT, cv) => node.children.get(cv),
|
||||
this.state.layoutDefinition,
|
||||
);
|
||||
return selectedNode ? selectedNode.children.size : 0;
|
||||
};
|
||||
|
||||
onToggleCode = () => {
|
||||
this.setState({
|
||||
selectedNodePath: this.state.showCode
|
||||
? this.state.selectedNodePath
|
||||
: null,
|
||||
showCode: !this.state.showCode,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {layoutDefinition, selectedNodePath} = this.state;
|
||||
const {height} = this.props;
|
||||
|
||||
const selectedNode: ?LayoutRecordT = selectedNodePath
|
||||
? layoutDefinition.getIn(getPath(selectedNodePath))
|
||||
: null;
|
||||
|
||||
const playground = (
|
||||
<div
|
||||
className="Playground"
|
||||
onMouseDown={this.onMouseDown}
|
||||
style={{height, maxHeight: height}}
|
||||
ref={ref => {
|
||||
this._containerRef = ref;
|
||||
}}>
|
||||
{/* <Toolbar
|
||||
onShowCode={
|
||||
!this.state.showCode
|
||||
? () => this.setState({selectedNodePath: null, showCode: true})
|
||||
: undefined
|
||||
}
|
||||
/> */}
|
||||
<YogaNode
|
||||
layoutDefinition={layoutDefinition}
|
||||
selectedNodePath={selectedNodePath}
|
||||
onClick={selectedNodePath =>
|
||||
this.setState({selectedNodePath, showCode: false})
|
||||
}
|
||||
onDoubleClick={this.onAdd}
|
||||
direction={this.state.direction}
|
||||
showGuides={this.props.showGuides}
|
||||
/>
|
||||
<Sidebar
|
||||
visible={
|
||||
Boolean(selectedNodePath) &&
|
||||
!this.state.showCode &&
|
||||
!this.props.renderSidebar
|
||||
}
|
||||
floating>
|
||||
<Editor
|
||||
node={selectedNode}
|
||||
selectedNodeIsRoot={
|
||||
selectedNodePath ? selectedNodePath.length === 0 : false
|
||||
}
|
||||
onChangeLayout={this.onChangeLayout}
|
||||
onChangeSetting={(key, value) => this.setState({[key]: value})}
|
||||
direction={this.state.direction}
|
||||
onRemove={
|
||||
selectedNodePath && selectedNodePath.length > 0
|
||||
? this.onRemove
|
||||
: undefined
|
||||
}
|
||||
onAdd={
|
||||
selectedNodePath && selectedNodePath.length < this.props.maxDepth
|
||||
? this.onAdd
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</Sidebar>
|
||||
<Sidebar
|
||||
width={500}
|
||||
visible={this.state.showCode}
|
||||
onClose={() => this.setState({showCode: false})}>
|
||||
{/* <Code
|
||||
layoutDefinition={layoutDefinition}
|
||||
direction={this.state.direction}
|
||||
/> */}
|
||||
</Sidebar>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (this.props.renderSidebar) {
|
||||
return (
|
||||
<div className={`PlaygroundContainer ${this.props.className || ''}`}>
|
||||
<div>
|
||||
{this.props.renderSidebar(
|
||||
this.state.layoutDefinition,
|
||||
this.onChangeLayout,
|
||||
)}
|
||||
</div>
|
||||
{playground}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return playground;
|
||||
}
|
||||
}
|
||||
}
|
43
website/src/components/Toolbar.css
Normal file
43
website/src/components/Toolbar.css
Normal file
@@ -0,0 +1,43 @@
|
||||
.Toolbar {
|
||||
border-bottom: 1px solid #dddfe2;
|
||||
box-shadow: 0 1px 6px 0 rgba(0, 0, 0, 0.1);
|
||||
background: white;
|
||||
height: 50px;
|
||||
padding: 0 10px;
|
||||
padding-bottom: 1px;
|
||||
z-index: 4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Toolbar .logo {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.Toolbar a {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.Toolbar h1 {
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 3px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.Toolbar .ToolbarSpacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Toolbar .ToolbarToggle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.Toolbar .ToolbarToggle i {
|
||||
margin-right: 5px;
|
||||
}
|
47
website/src/components/Toolbar.js
Normal file
47
website/src/components/Toolbar.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* 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 Link from 'gatsby-link';
|
||||
import {Icon} from 'antd';
|
||||
import './Toolbar.css';
|
||||
|
||||
type Props = {
|
||||
onShowCode?: () => void,
|
||||
};
|
||||
|
||||
export default class Toolbar extends Component<Props> {
|
||||
render() {
|
||||
return (
|
||||
<div className="Toolbar">
|
||||
<Link to="/" className="logo">
|
||||
<img
|
||||
src="https://facebook.github.io/yoga/static/logo.svg"
|
||||
width="42"
|
||||
alt="Yoga logo"
|
||||
/>
|
||||
<h1>Yoga Layout</h1>
|
||||
</Link>
|
||||
<div className="ToolbarSpacer" />
|
||||
<Link to="/docs">Docs</Link>
|
||||
<Link to="/playground">Playground</Link>
|
||||
<a href="https://github.com/facebook/yoga">GitHub</a>
|
||||
{this.props.onShowCode && (
|
||||
<a className="ToolbarToggle" onClick={this.props.onShowCode}>
|
||||
<Icon type={'code-o'} />
|
||||
Code
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
22
website/src/pages/404.js
Normal file
22
website/src/pages/404.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
|
||||
const NotFoundPage = () => (
|
||||
<div>
|
||||
<h1>NOT FOUND</h1>
|
||||
<p>You just hit a route that doesn't exist... the sadness.</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default NotFoundPage;
|
93
website/src/pages/docs/index.js
Normal file
93
website/src/pages/docs/index.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Link from 'gatsby-link';
|
||||
import Page from '../../components/Page';
|
||||
import Padded from '../../components/Padded';
|
||||
import {Row, Col} from 'antd';
|
||||
|
||||
export default () => (
|
||||
<Page>
|
||||
<Padded>
|
||||
<h1>Documentation</h1>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis
|
||||
urna in lacus rutrum vehicula. Curabitur vitae dictum ante, sollicitudin
|
||||
sollicitudin tortor. Morbi a vehicula justo. Proin luctus tellus vitae
|
||||
nulla bibendum gravida. Duis varius cursus erat ut tempus. Fusce ex
|
||||
arcu, accumsan et ultricies at, fermentum sit amet lectus. Donec a neque
|
||||
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>
|
||||
</Row>
|
||||
</Padded>
|
||||
</Page>
|
||||
);
|
12
website/src/pages/index.css
Normal file
12
website/src/pages/index.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.IndexPageSection {
|
||||
padding-top: 80px;
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
|
||||
.IndexPageSection h1 {
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
.IndexPageSection .button-spacing {
|
||||
margin-right: 10px;
|
||||
}
|
71
website/src/pages/index.js
Normal file
71
website/src/pages/index.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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 {Button} from 'antd';
|
||||
import React from 'react';
|
||||
import Page from '../components/Page';
|
||||
import Padded from '../components/Padded';
|
||||
import Playground from '../components/Playground';
|
||||
import {Row, Col} from 'antd';
|
||||
import './index.css';
|
||||
|
||||
export default () => (
|
||||
<Page>
|
||||
<Padded className="IndexPageSection">
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<h1>Flexible Layouts with Yoga</h1>
|
||||
<p>
|
||||
Build flexible layouts on any platform with a highly optimized
|
||||
layout engine designed with speed, size, and ease of use in mind.
|
||||
</p>
|
||||
<Button type="primary" className="button-spacing">
|
||||
Get started
|
||||
</Button>
|
||||
<Button type="primary" ghost>
|
||||
Try it out
|
||||
</Button>
|
||||
</Col>
|
||||
<Col span={12} />
|
||||
</Row>
|
||||
</Padded>
|
||||
<Playground selectedNodePath={[]} showGuides={false} height={501} />
|
||||
<Padded className="IndexPageSection">
|
||||
<Row>
|
||||
<Col span={12} />
|
||||
<Col span={12}>
|
||||
<h1>Integrated Into The Most Optimized Mobile Frameworks</h1>
|
||||
<p>
|
||||
Facebook makes use of Yoga to power most of their UI Frameworks.
|
||||
This ensured layout can be performed quickly and fluidly off of the
|
||||
main thread to ensure optimal performance. As an added benefit
|
||||
engineers familiar with one framework can get started quickly with
|
||||
another thanks to the shared layout engine.
|
||||
</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</Padded>
|
||||
<Padded className="IndexPageSection">
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<h1>Proven In Productions</h1>
|
||||
<p>
|
||||
Yoga powers some of the largest apps in the world, including many of
|
||||
Facebook's products. This should help convince you that Yoga is
|
||||
ready to handle the scale of almost any app out there.
|
||||
</p>
|
||||
</Col>
|
||||
<Col span={12} />
|
||||
</Row>
|
||||
</Padded>
|
||||
</Page>
|
||||
);
|
21
website/src/pages/playground.js
Normal file
21
website/src/pages/playground.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Page from '../components/Page';
|
||||
import Playground from '../components/Playground';
|
||||
|
||||
export default () => (
|
||||
<Page title="Playground">
|
||||
<Playground height="100%" />
|
||||
</Page>
|
||||
);
|
38
website/src/templates/withPlayground.js
Normal file
38
website/src/templates/withPlayground.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Page from '../components/Page';
|
||||
import Playground from '../components/Playground';
|
||||
import DocsSidebar from '../components/DocsSidebar';
|
||||
import YogaEnumSelect from '../components/Playground/YogaEnumSelect';
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
25
website/src/templates/withoutPlayground.js
Normal file
25
website/src/templates/withoutPlayground.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 from 'react';
|
||||
import Page from '../components/Page';
|
||||
import Padded from '../components/Padded';
|
||||
|
||||
export default ({pathContext}) => {
|
||||
return (
|
||||
<Page>
|
||||
<Padded>
|
||||
<div dangerouslySetInnerHTML={{__html: pathContext.html}} />
|
||||
</Padded>
|
||||
</Page>
|
||||
);
|
||||
};
|
9813
website/yarn.lock
Normal file
9813
website/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user