add share and code buttons
Summary: Adds code and share buttons to the editor Reviewed By: emilsjolander Differential Revision: D6989097 fbshipit-source-id: 67478fe0810a0af43524f24458c520acf2999219
This commit is contained in:
committed by
Facebook Github Bot
parent
b1222bf83e
commit
43fda26275
@@ -1,29 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
@@ -1,114 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
110
website/src/components/Playground/CodeGenerators.js
Normal file
110
website/src/components/Playground/CodeGenerators.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/**
|
||||||
|
* 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 {Menu, Button, Row, Col, Dropdown, Icon, Modal} from 'antd';
|
||||||
|
import SyntaxHighlighter, {
|
||||||
|
registerLanguage,
|
||||||
|
} from 'react-syntax-highlighter/prism-light';
|
||||||
|
import styles from 'react-syntax-highlighter/styles/prism/prism';
|
||||||
|
import CodeJavaScript from './CodeJavaScript';
|
||||||
|
import CodeLitho from './CodeLitho';
|
||||||
|
import CodeReactNative from './CodeReactNative';
|
||||||
|
import CodeComponentKit from './CodeComponentKit';
|
||||||
|
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';
|
||||||
|
registerLanguage('jsx', jsx);
|
||||||
|
//registerLanguage('javascript', javascript);
|
||||||
|
registerLanguage('java', java);
|
||||||
|
registerLanguage('objectivec', objectivec);
|
||||||
|
|
||||||
|
import type {LayoutRecordT} from './LayoutRecord';
|
||||||
|
import type {Yoga$Direction} from 'yoga-layout';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layoutDefinition: LayoutRecordT,
|
||||||
|
direction: Yoga$Direction,
|
||||||
|
};
|
||||||
|
type State = {
|
||||||
|
showModal: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const LANGUAGES = {
|
||||||
|
litho: {
|
||||||
|
title: 'Litho',
|
||||||
|
generator: CodeLitho,
|
||||||
|
syntax: 'java',
|
||||||
|
},
|
||||||
|
componentKit: {
|
||||||
|
title: 'ComponentKit',
|
||||||
|
generator: CodeComponentKit,
|
||||||
|
syntax: 'objectivec',
|
||||||
|
},
|
||||||
|
reactNative: {
|
||||||
|
title: 'React Native',
|
||||||
|
generator: CodeReactNative,
|
||||||
|
syntax: 'jsx',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
['Litho', 'ComponentKit', 'React Native'];
|
||||||
|
|
||||||
|
export default class CodeGenerators extends Component<Props, State> {
|
||||||
|
state = {
|
||||||
|
showModal: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
onClick = ({key}: {key: string}) => this.setState({showModal: key});
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {showModal} = this.state;
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu onClick={this.onClick}>
|
||||||
|
{Object.keys(LANGUAGES).map(key => (
|
||||||
|
<Menu.Item key={key}>{LANGUAGES[key].title}</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
<Modal
|
||||||
|
key="modal"
|
||||||
|
title={showModal ? LANGUAGES[showModal].title : ''}
|
||||||
|
visible={Boolean(showModal)}
|
||||||
|
footer={null}
|
||||||
|
bodyStyle={{padding: 0}}
|
||||||
|
onCancel={() => this.setState({showModal: null})}>
|
||||||
|
{showModal && (
|
||||||
|
<SyntaxHighlighter
|
||||||
|
language={LANGUAGES[showModal].syntax}
|
||||||
|
style={styles}
|
||||||
|
customStyle={{fontSize: '13px', backgroundColor: 'white'}}
|
||||||
|
lineNumberStyle={{userSelect: 'none', opacity: 0.5}}
|
||||||
|
codeTagProps={{style: {tabSize: 4}}}
|
||||||
|
showLineNumbers>
|
||||||
|
{LANGUAGES[showModal].generator(
|
||||||
|
this.props.layoutDefinition,
|
||||||
|
this.props.direction,
|
||||||
|
)}
|
||||||
|
</SyntaxHighlighter>
|
||||||
|
)}
|
||||||
|
</Modal>,
|
||||||
|
<Dropdown overlay={menu} key="dropdown">
|
||||||
|
<Button>
|
||||||
|
Get Code <Icon type="down" />
|
||||||
|
</Button>
|
||||||
|
</Dropdown>,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@@ -11,6 +11,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border-top: 1px solid #E8E8E8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Editor .field {
|
.Editor .field {
|
||||||
|
@@ -58,7 +58,9 @@ export default class Editor extends Component<Props> {
|
|||||||
<TabPane tab="Flex" key="1">
|
<TabPane tab="Flex" key="1">
|
||||||
<h2>
|
<h2>
|
||||||
Direction
|
Direction
|
||||||
<InfoText doclink="/docs/layout-direction">Defines the direction of which text and items are laid out</InfoText>
|
<InfoText doclink="/docs/layout-direction">
|
||||||
|
Defines the direction of which text and items are laid out
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
property="direction"
|
property="direction"
|
||||||
@@ -67,7 +69,9 @@ export default class Editor extends Component<Props> {
|
|||||||
/>
|
/>
|
||||||
<h2>
|
<h2>
|
||||||
Flex direction
|
Flex direction
|
||||||
<InfoText doclink="/docs/flex-direction">Defines the direction of the main-axis</InfoText>
|
<InfoText doclink="/docs/flex-direction">
|
||||||
|
Defines the direction of the main-axis
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -77,10 +81,27 @@ export default class Editor extends Component<Props> {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Row gutter={15} style={{marginTop: 30}}>
|
<Row gutter={15} style={{marginTop: 30}}>
|
||||||
<Col span={12}>
|
<Col span={8}>
|
||||||
<h2>
|
<h2>
|
||||||
Flex grow
|
Basis
|
||||||
<InfoText doclink="/docs/flex">The factor of remaining space should be given to this node</InfoText>
|
<InfoText doclink="/docs/flex">
|
||||||
|
The factor of remaining space should be given to this node
|
||||||
|
</InfoText>
|
||||||
|
</h2>
|
||||||
|
<EditValue
|
||||||
|
type="text"
|
||||||
|
property="flexBasis"
|
||||||
|
disabled={disabled || selectedNodeIsRoot}
|
||||||
|
value={node ? node.flexBasis : undefined}
|
||||||
|
onChange={this.props.onChangeLayout}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<h2>
|
||||||
|
Grow
|
||||||
|
<InfoText doclink="/docs/flex">
|
||||||
|
The factor of remaining space should be given to this node
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
type="text"
|
type="text"
|
||||||
@@ -90,10 +111,13 @@ export default class Editor extends Component<Props> {
|
|||||||
onChange={this.props.onChangeLayout}
|
onChange={this.props.onChangeLayout}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={8}>
|
||||||
<h2>
|
<h2>
|
||||||
Flex shrink
|
Shrink
|
||||||
<InfoText doclink="/docs/flex">The shrink factor of this element if parent has no space left</InfoText>
|
<InfoText doclink="/docs/flex">
|
||||||
|
The shrink factor of this element if parent has no space
|
||||||
|
left
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
type="text"
|
type="text"
|
||||||
@@ -107,7 +131,9 @@ export default class Editor extends Component<Props> {
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Flex wrap
|
Flex wrap
|
||||||
<InfoText doclink="/docs/flex-wrap">Wrapping behaviour when child nodes don't fit into a single line</InfoText>
|
<InfoText doclink="/docs/flex-wrap">
|
||||||
|
Wrapping behaviour when child nodes don't fit into a single line
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -119,7 +145,9 @@ export default class Editor extends Component<Props> {
|
|||||||
<TabPane tab="Alignment" key="2">
|
<TabPane tab="Alignment" key="2">
|
||||||
<h2>
|
<h2>
|
||||||
Justify content
|
Justify content
|
||||||
<InfoText doclink="/docs/justify-content">Aligns child nodes along the main-axis</InfoText>
|
<InfoText doclink="/docs/justify-content">
|
||||||
|
Aligns child nodes along the main-axis
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -130,7 +158,9 @@ export default class Editor extends Component<Props> {
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Align items
|
Align items
|
||||||
<InfoText doclink="/docs/align-items">Aligns child nodes along the cross-axis</InfoText>
|
<InfoText doclink="/docs/align-items">
|
||||||
|
Aligns child nodes along the cross-axis
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -141,7 +171,9 @@ export default class Editor extends Component<Props> {
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Align self
|
Align self
|
||||||
<InfoText doclink="/docs/align-items">Override align items of parent</InfoText>
|
<InfoText doclink="/docs/align-items">
|
||||||
|
Override align items of parent
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled || selectedNodeIsRoot}
|
disabled={disabled || selectedNodeIsRoot}
|
||||||
@@ -152,7 +184,9 @@ export default class Editor extends Component<Props> {
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Align content
|
Align content
|
||||||
<InfoText doclink="/docs/align-content">Alignment of lines along the cross-axis when wrapping</InfoText>
|
<InfoText doclink="/docs/align-content">
|
||||||
|
Alignment of lines along the cross-axis when wrapping
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -164,13 +198,15 @@ export default class Editor extends Component<Props> {
|
|||||||
<TabPane tab="Layout" key="3">
|
<TabPane tab="Layout" key="3">
|
||||||
<h2>
|
<h2>
|
||||||
Width × height
|
Width × height
|
||||||
<InfoText doclink="/docs/width-height">Dimensions of the node</InfoText>
|
<InfoText doclink="/docs/width-height">
|
||||||
|
Dimensions of the node
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<Row gutter={15}>
|
<Row gutter={15}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<EditValue
|
<EditValue
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="width"
|
placeholder="auto"
|
||||||
property="width"
|
property="width"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={node ? node.width : undefined}
|
value={node ? node.width : undefined}
|
||||||
@@ -180,7 +216,7 @@ export default class Editor extends Component<Props> {
|
|||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<EditValue
|
<EditValue
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="height"
|
placeholder="auto"
|
||||||
property="height"
|
property="height"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={node ? node.height : undefined}
|
value={node ? node.height : undefined}
|
||||||
@@ -191,11 +227,13 @@ export default class Editor extends Component<Props> {
|
|||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
Aspect ratio
|
Aspect ratio
|
||||||
<InfoText doclink="/docs/aspect-ratio">Width/Height aspect ratio of node</InfoText>
|
<InfoText doclink="/docs/aspect-ratio">
|
||||||
|
Width/Height aspect ratio of node
|
||||||
|
</InfoText>
|
||||||
</h2>
|
</h2>
|
||||||
<EditValue
|
<EditValue
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Aspect ratio"
|
placeholder="none"
|
||||||
property="aspectRatio"
|
property="aspectRatio"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={node ? node.aspectRatio : undefined}
|
value={node ? node.aspectRatio : undefined}
|
||||||
@@ -209,6 +247,7 @@ export default class Editor extends Component<Props> {
|
|||||||
key={property}
|
key={property}
|
||||||
value={node ? node[property] : undefined}
|
value={node ? node[property] : undefined}
|
||||||
onChange={this.props.onChangeLayout}
|
onChange={this.props.onChangeLayout}
|
||||||
|
disabled={property === 'margin' && selectedNodeIsRoot}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<h2>
|
<h2>
|
||||||
@@ -221,12 +260,13 @@ export default class Editor extends Component<Props> {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<EditValue
|
<EditValue
|
||||||
disabled={disabled}
|
disabled={disabled || selectedNodeIsRoot}
|
||||||
property="positionType"
|
property="positionType"
|
||||||
value={node ? node.positionType : undefined}
|
value={node ? node.positionType : undefined}
|
||||||
onChange={this.props.onChangeLayout}
|
onChange={this.props.onChangeLayout}
|
||||||
/>
|
/>
|
||||||
<EditValue
|
<EditValue
|
||||||
|
disabled={selectedNodeIsRoot}
|
||||||
property="position"
|
property="position"
|
||||||
value={node ? node.position : undefined}
|
value={node ? node.position : undefined}
|
||||||
onChange={this.props.onChangeLayout}
|
onChange={this.props.onChangeLayout}
|
||||||
|
@@ -26,11 +26,10 @@ export default class InfoText extends Component<Props> {
|
|||||||
<Popover
|
<Popover
|
||||||
content={
|
content={
|
||||||
<div className="InfoText">
|
<div className="InfoText">
|
||||||
{
|
<p>{this.props.children}</p>
|
||||||
[
|
<Link className="docs-link" to={this.props.doclink}>
|
||||||
<p>{[...this.props.children]}</p>,
|
DOCUMENTATION
|
||||||
<Link className="docs-link" to={this.props.doclink}>DOCUMENTATION</Link>]
|
</Link>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
trigger="hover">
|
trigger="hover">
|
||||||
|
@@ -21,7 +21,7 @@ import type {
|
|||||||
Yoga$JustifyContent,
|
Yoga$JustifyContent,
|
||||||
Yoga$FlexDirection,
|
Yoga$FlexDirection,
|
||||||
Yoga$FlexWrap,
|
Yoga$FlexWrap,
|
||||||
Yoga$Yoga$PositionType,
|
Yoga$PositionType,
|
||||||
} from 'yoga-layout';
|
} from 'yoga-layout';
|
||||||
|
|
||||||
export type LayoutRecordT = RecordOf<{
|
export type LayoutRecordT = RecordOf<{
|
||||||
@@ -32,17 +32,18 @@ export type LayoutRecordT = RecordOf<{
|
|||||||
border: PositionRecordT,
|
border: PositionRecordT,
|
||||||
margin: PositionRecordT,
|
margin: PositionRecordT,
|
||||||
position: PositionRecordT,
|
position: PositionRecordT,
|
||||||
positionType: Yoga$Yoga$PositionType,
|
positionType: Yoga$PositionType,
|
||||||
alignItems?: Yoga$Align,
|
alignItems?: Yoga$Align,
|
||||||
alignSelf?: Yoga$Align,
|
alignSelf?: Yoga$Align,
|
||||||
alignContent?: Yoga$Align,
|
alignContent?: Yoga$Align,
|
||||||
flexDirection?: Yoga$FlexDirection,
|
flexDirection?: Yoga$FlexDirection,
|
||||||
|
flexBasis?: number | 'auto',
|
||||||
flexGrow?: number,
|
flexGrow?: number,
|
||||||
flexShrink?: number,
|
flexShrink?: number,
|
||||||
padding?: number,
|
padding?: number,
|
||||||
flexWrap?: Yoga$FlexWrap,
|
flexWrap?: Yoga$FlexWrap,
|
||||||
aspectRatio?: number,
|
aspectRatio?: number,
|
||||||
children?: List<LayoutDefinition>,
|
children?: List<LayoutRecordT>,
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const r: LayoutRecordT = Record({
|
const r: LayoutRecordT = Record({
|
||||||
@@ -59,6 +60,7 @@ const r: LayoutRecordT = Record({
|
|||||||
position: PositionRecord(),
|
position: PositionRecord(),
|
||||||
positionType: yoga.POSITION_TYPE_RELATIVE,
|
positionType: yoga.POSITION_TYPE_RELATIVE,
|
||||||
flexWrap: yoga.WRAP_NO_WRAP,
|
flexWrap: yoga.WRAP_NO_WRAP,
|
||||||
|
flexBasis: 'auto',
|
||||||
flexGrow: 0,
|
flexGrow: 0,
|
||||||
flexShrink: 1,
|
flexShrink: 1,
|
||||||
children: List(),
|
children: List(),
|
||||||
|
@@ -12,50 +12,13 @@
|
|||||||
z-index: 3;
|
z-index: 3;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
|
||||||
width: 350px;
|
width: 350px;
|
||||||
background: white;
|
background: white;
|
||||||
border-left: 1px solid #dddfe2;
|
|
||||||
transform: translateX(100%);
|
|
||||||
transition: 0.2s transform;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Sidebar.floating {
|
|
||||||
margin: 25px;
|
margin: 25px;
|
||||||
max-height: calc(100% - 50px);
|
max-height: calc(100% - 50px);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
box-shadow: 3px 3px 15px rgba(0, 0, 0, 0.15);
|
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;
|
|
||||||
}
|
}
|
||||||
|
@@ -11,29 +11,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import {Icon} from 'antd';
|
|
||||||
import './Sidebar.css';
|
import './Sidebar.css';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClose?: () => void,
|
|
||||||
width?: number,
|
width?: number,
|
||||||
children: any,
|
children: any,
|
||||||
floating?: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Sidebar extends Component<Props> {
|
export default class Sidebar extends Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="Sidebar" style={{width: this.props.width}}>
|
||||||
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}
|
{this.props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -1,7 +1,2 @@
|
|||||||
.URLShortener {
|
.URLShortener {
|
||||||
position: absolute;
|
|
||||||
bottom: 30px;
|
|
||||||
left: 30px;
|
|
||||||
z-index: 4;
|
|
||||||
box-shadow: 1px 1px 15px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
}
|
||||||
|
@@ -16,53 +16,75 @@ import './URLShortener.css';
|
|||||||
|
|
||||||
const API_KEY = 'AIzaSyCRvdtNY07SGUokChS8oA9EaYJafFL0zMI';
|
const API_KEY = 'AIzaSyCRvdtNY07SGUokChS8oA9EaYJafFL0zMI';
|
||||||
|
|
||||||
export default class URLShortener extends Component<{}> {
|
type State = {
|
||||||
|
shortURL: ?string,
|
||||||
|
loading: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class URLShortener extends Component<{}, State> {
|
||||||
_ref: ?HTMLElement = null;
|
_ref: ?HTMLElement = null;
|
||||||
|
|
||||||
|
state = {
|
||||||
|
loading: false,
|
||||||
|
shortURL: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('hashchange', this.onHashChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('hashchange', this.onHashChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
onHashChange = () => {
|
||||||
|
this.setState({shortURL: null});
|
||||||
|
};
|
||||||
|
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${API_KEY}`, {
|
this.setState(
|
||||||
method: 'POST',
|
{
|
||||||
headers: {
|
loading: true,
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
() => {
|
||||||
kind: 'urlshortener#url',
|
fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${API_KEY}`, {
|
||||||
longUrl: window.location.href,
|
method: 'POST',
|
||||||
}),
|
headers: {
|
||||||
})
|
Accept: 'application/json',
|
||||||
.then(res => res.json())
|
'Content-Type': 'application/json',
|
||||||
.then(({id}) => {
|
},
|
||||||
notification.open({
|
body: JSON.stringify({
|
||||||
message: 'Created short URL',
|
kind: 'urlshortener#url',
|
||||||
description: (
|
longUrl: window.location.href,
|
||||||
<Input
|
}),
|
||||||
value={id}
|
})
|
||||||
autoFocus
|
.then(res => res.json())
|
||||||
ref={ref => {
|
.then(({id}) => this.setState({shortURL: id, loading: false}))
|
||||||
if (ref) {
|
.catch(() => this.setState({shortURL: null, loading: false}));
|
||||||
ref.input.select();
|
},
|
||||||
}
|
);
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
placement: 'bottomLeft',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return this.state.shortURL ? (
|
||||||
<Tooltip title="Copy URL">
|
<Input
|
||||||
<Button
|
value={this.state.shortURL}
|
||||||
className="URLShortener"
|
autoFocus
|
||||||
onClick={this.onClick}
|
ref={ref => {
|
||||||
icon="share-alt"
|
if (ref) {
|
||||||
type="primary"
|
ref.input.select();
|
||||||
shape="circle"
|
}
|
||||||
size="large"
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
) : (
|
||||||
|
<Button
|
||||||
|
className="URLShortener"
|
||||||
|
onClick={this.onClick}
|
||||||
|
icon="share-alt"
|
||||||
|
disabled={this.state.loading}
|
||||||
|
type="primary">
|
||||||
|
Share URL
|
||||||
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ type Property = 'position' | 'margin' | 'padding' | 'border';
|
|||||||
type Props = {
|
type Props = {
|
||||||
value: PositionRecordT,
|
value: PositionRecordT,
|
||||||
property: Property,
|
property: Property,
|
||||||
|
disabled?: boolean,
|
||||||
onChange: (property: Property, value: PositionRecordT) => void,
|
onChange: (property: Property, value: PositionRecordT) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,18 +33,22 @@ export default class YogaPositionEditor extends Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {onChange, value, property} = this.props;
|
const {onChange, value, property, disabled} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="YogaPositionEditor">
|
<div className="YogaPositionEditor">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={value.top}
|
value={value.top}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="0"
|
||||||
onChange={e => onChange(property, value.set('top', e.target.value))}
|
onChange={e => onChange(property, value.set('top', e.target.value))}
|
||||||
/>
|
/>
|
||||||
<div className="YogaPositionEditorRow">
|
<div className="YogaPositionEditorRow">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={value.left}
|
value={value.left}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="0"
|
||||||
onChange={e =>
|
onChange={e =>
|
||||||
onChange(property, value.set('left', e.target.value))
|
onChange(property, value.set('left', e.target.value))
|
||||||
}
|
}
|
||||||
@@ -52,6 +57,8 @@ export default class YogaPositionEditor extends Component<Props> {
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={value.right}
|
value={value.right}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="0"
|
||||||
onChange={e =>
|
onChange={e =>
|
||||||
onChange(property, value.set('right', e.target.value))
|
onChange(property, value.set('right', e.target.value))
|
||||||
}
|
}
|
||||||
@@ -60,6 +67,8 @@ export default class YogaPositionEditor extends Component<Props> {
|
|||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
value={value.bottom}
|
value={value.bottom}
|
||||||
|
disabled={disabled}
|
||||||
|
placeholder="0"
|
||||||
onChange={e =>
|
onChange={e =>
|
||||||
onChange(property, value.set('bottom', e.target.value))
|
onChange(property, value.set('bottom', e.target.value))
|
||||||
}
|
}
|
||||||
|
@@ -43,9 +43,30 @@
|
|||||||
100px 100px, 100px 100px, 100px 100px;
|
100px 100px, 100px 100px, 100px 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.Playground > .YogaNode {
|
.Playground > .YogaNode {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
position: static;
|
position: static;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Playground .Actions {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Playground .Actions .ant-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Playground .NoContent {
|
||||||
|
border-top: 1px solid #E8E8E8;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 30px 50px;
|
||||||
|
text-align: center;
|
||||||
|
color: #D9D9D9;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 130%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
@@ -13,13 +13,14 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import yoga from 'yoga-layout/dist/entry-browser';
|
import yoga from 'yoga-layout/dist/entry-browser';
|
||||||
import YogaNode from './YogaNode';
|
import YogaNode from './YogaNode';
|
||||||
|
import CodeGenerators from './CodeGenerators';
|
||||||
|
import URLShortener from './URLShortener';
|
||||||
import Editor from './Editor';
|
import Editor from './Editor';
|
||||||
import {List, setIn} from 'immutable';
|
import {List, setIn} from 'immutable';
|
||||||
import PositionRecord from './PositionRecord';
|
import PositionRecord from './PositionRecord';
|
||||||
import LayoutRecord from './LayoutRecord';
|
import LayoutRecord from './LayoutRecord';
|
||||||
import URLShortener from './URLShortener';
|
|
||||||
import Code from './Code';
|
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
|
import {Row, Col} from 'antd';
|
||||||
import type {LayoutRecordT} from './LayoutRecord';
|
import type {LayoutRecordT} from './LayoutRecord';
|
||||||
import type {Yoga$Direction} from 'yoga-layout';
|
import type {Yoga$Direction} from 'yoga-layout';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
@@ -42,7 +43,6 @@ type State = {
|
|||||||
selectedNodePath: ?Array<number>,
|
selectedNodePath: ?Array<number>,
|
||||||
layoutDefinition: LayoutRecordT,
|
layoutDefinition: LayoutRecordT,
|
||||||
direction: Yoga$Direction,
|
direction: Yoga$Direction,
|
||||||
showCode: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPath(path: Array<number>): Array<mixed> {
|
function getPath(path: Array<number>): Array<mixed> {
|
||||||
@@ -60,7 +60,6 @@ export default class Playground extends Component<Props, State> {
|
|||||||
},
|
},
|
||||||
direction: yoga.DIRECTION_LTR,
|
direction: yoga.DIRECTION_LTR,
|
||||||
maxDepth: 3,
|
maxDepth: 3,
|
||||||
showCode: false,
|
|
||||||
showGuides: true,
|
showGuides: true,
|
||||||
persist: false,
|
persist: false,
|
||||||
};
|
};
|
||||||
@@ -79,7 +78,6 @@ export default class Playground extends Component<Props, State> {
|
|||||||
selectedNodePath: this.props.selectedNodePath,
|
selectedNodePath: this.props.selectedNodePath,
|
||||||
layoutDefinition: this.rehydrate(this.props.layoutDefinition),
|
layoutDefinition: this.rehydrate(this.props.layoutDefinition),
|
||||||
direction: this.props.direction,
|
direction: this.props.direction,
|
||||||
showCode: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -118,7 +116,6 @@ export default class Playground extends Component<Props, State> {
|
|||||||
// sidebar may rely on a certain node to be selected
|
// sidebar may rely on a certain node to be selected
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedNodePath: null,
|
selectedNodePath: null,
|
||||||
showCode: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -211,17 +208,8 @@ export default class Playground extends Component<Props, State> {
|
|||||||
return selectedNode ? selectedNode.children.size : 0;
|
return selectedNode ? selectedNode.children.size : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
onToggleCode = () => {
|
|
||||||
this.setState({
|
|
||||||
selectedNodePath: this.state.showCode
|
|
||||||
? this.state.selectedNodePath
|
|
||||||
: null,
|
|
||||||
showCode: !this.state.showCode,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {layoutDefinition, selectedNodePath} = this.state;
|
const {layoutDefinition, selectedNodePath, direction} = this.state;
|
||||||
const {height} = this.props;
|
const {height} = this.props;
|
||||||
|
|
||||||
const selectedNode: ?LayoutRecordT = selectedNodePath
|
const selectedNode: ?LayoutRecordT = selectedNodePath
|
||||||
@@ -239,46 +227,52 @@ export default class Playground extends Component<Props, State> {
|
|||||||
<YogaNode
|
<YogaNode
|
||||||
layoutDefinition={layoutDefinition}
|
layoutDefinition={layoutDefinition}
|
||||||
selectedNodePath={selectedNodePath}
|
selectedNodePath={selectedNodePath}
|
||||||
onClick={selectedNodePath =>
|
onClick={selectedNodePath => this.setState({selectedNodePath})}
|
||||||
this.setState({selectedNodePath, showCode: false})
|
|
||||||
}
|
|
||||||
onDoubleClick={this.onAdd}
|
onDoubleClick={this.onAdd}
|
||||||
direction={this.state.direction}
|
direction={direction}
|
||||||
showGuides={this.props.showGuides}
|
showGuides={this.props.showGuides}
|
||||||
/>
|
/>
|
||||||
<Sidebar
|
<Sidebar>
|
||||||
visible={
|
<div className="Actions">
|
||||||
Boolean(selectedNodePath) &&
|
<Row gutter={15}>
|
||||||
!this.state.showCode &&
|
<Col span={12}>
|
||||||
!this.props.renderSidebar
|
<CodeGenerators
|
||||||
}
|
layoutDefinition={layoutDefinition}
|
||||||
floating>
|
direction={direction}
|
||||||
<Editor
|
/>
|
||||||
node={selectedNode}
|
</Col>
|
||||||
selectedNodeIsRoot={
|
<Col span={12}>
|
||||||
selectedNodePath ? selectedNodePath.length === 0 : false
|
<URLShortener />
|
||||||
}
|
</Col>
|
||||||
onChangeLayout={this.onChangeLayout}
|
</Row>
|
||||||
onChangeSetting={(key, value) => this.setState({[key]: value})}
|
</div>
|
||||||
direction={this.state.direction}
|
{this.state.selectedNodePath ? (
|
||||||
onRemove={
|
<Editor
|
||||||
selectedNodePath && selectedNodePath.length > 0
|
node={selectedNode}
|
||||||
? this.onRemove
|
selectedNodeIsRoot={
|
||||||
: undefined
|
selectedNodePath ? selectedNodePath.length === 0 : false
|
||||||
}
|
}
|
||||||
onAdd={
|
onChangeLayout={this.onChangeLayout}
|
||||||
selectedNodePath && selectedNodePath.length < this.props.maxDepth
|
onChangeSetting={(key, value) => this.setState({[key]: value})}
|
||||||
? this.onAdd
|
direction={direction}
|
||||||
: undefined
|
onRemove={
|
||||||
}
|
selectedNodePath && selectedNodePath.length > 0
|
||||||
/>
|
? this.onRemove
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onAdd={
|
||||||
|
selectedNodePath &&
|
||||||
|
selectedNodePath.length < this.props.maxDepth
|
||||||
|
? this.onAdd
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="NoContent">
|
||||||
|
Select a node to edit its properties
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
<Sidebar
|
|
||||||
width={500}
|
|
||||||
visible={this.state.showCode}
|
|
||||||
onClose={() => this.setState({showCode: false})}
|
|
||||||
/>
|
|
||||||
{this.props.persist && <URLShortener />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user