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;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
border-top: 1px solid #E8E8E8;
|
||||
}
|
||||
|
||||
.Editor .field {
|
||||
|
@@ -58,7 +58,9 @@ export default class Editor extends Component<Props> {
|
||||
<TabPane tab="Flex" key="1">
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
property="direction"
|
||||
@@ -67,7 +69,9 @@ export default class Editor extends Component<Props> {
|
||||
/>
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
@@ -77,10 +81,27 @@ export default class Editor extends Component<Props> {
|
||||
/>
|
||||
|
||||
<Row gutter={15} style={{marginTop: 30}}>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<h2>
|
||||
Flex grow
|
||||
<InfoText doclink="/docs/flex">The factor of remaining space should be given to this node</InfoText>
|
||||
Basis
|
||||
<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>
|
||||
<EditValue
|
||||
type="text"
|
||||
@@ -90,10 +111,13 @@ export default class Editor extends Component<Props> {
|
||||
onChange={this.props.onChangeLayout}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<h2>
|
||||
Flex shrink
|
||||
<InfoText doclink="/docs/flex">The shrink factor of this element if parent has no space left</InfoText>
|
||||
Shrink
|
||||
<InfoText doclink="/docs/flex">
|
||||
The shrink factor of this element if parent has no space
|
||||
left
|
||||
</InfoText>
|
||||
</h2>
|
||||
<EditValue
|
||||
type="text"
|
||||
@@ -107,7 +131,9 @@ export default class Editor extends Component<Props> {
|
||||
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
@@ -119,7 +145,9 @@ export default class Editor extends Component<Props> {
|
||||
<TabPane tab="Alignment" key="2">
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
@@ -130,7 +158,9 @@ export default class Editor extends Component<Props> {
|
||||
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
@@ -141,7 +171,9 @@ export default class Editor extends Component<Props> {
|
||||
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled || selectedNodeIsRoot}
|
||||
@@ -152,7 +184,9 @@ export default class Editor extends Component<Props> {
|
||||
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
@@ -164,13 +198,15 @@ export default class Editor extends Component<Props> {
|
||||
<TabPane tab="Layout" key="3">
|
||||
<h2>
|
||||
Width × height
|
||||
<InfoText doclink="/docs/width-height">Dimensions of the node</InfoText>
|
||||
<InfoText doclink="/docs/width-height">
|
||||
Dimensions of the node
|
||||
</InfoText>
|
||||
</h2>
|
||||
<Row gutter={15}>
|
||||
<Col span={12}>
|
||||
<EditValue
|
||||
type="text"
|
||||
placeholder="width"
|
||||
placeholder="auto"
|
||||
property="width"
|
||||
disabled={disabled}
|
||||
value={node ? node.width : undefined}
|
||||
@@ -180,7 +216,7 @@ export default class Editor extends Component<Props> {
|
||||
<Col span={12}>
|
||||
<EditValue
|
||||
type="text"
|
||||
placeholder="height"
|
||||
placeholder="auto"
|
||||
property="height"
|
||||
disabled={disabled}
|
||||
value={node ? node.height : undefined}
|
||||
@@ -191,11 +227,13 @@ export default class Editor extends Component<Props> {
|
||||
|
||||
<h2>
|
||||
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>
|
||||
<EditValue
|
||||
type="text"
|
||||
placeholder="Aspect ratio"
|
||||
placeholder="none"
|
||||
property="aspectRatio"
|
||||
disabled={disabled}
|
||||
value={node ? node.aspectRatio : undefined}
|
||||
@@ -209,6 +247,7 @@ export default class Editor extends Component<Props> {
|
||||
key={property}
|
||||
value={node ? node[property] : undefined}
|
||||
onChange={this.props.onChangeLayout}
|
||||
disabled={property === 'margin' && selectedNodeIsRoot}
|
||||
/>
|
||||
))}
|
||||
<h2>
|
||||
@@ -221,12 +260,13 @@ export default class Editor extends Component<Props> {
|
||||
</h2>
|
||||
|
||||
<EditValue
|
||||
disabled={disabled}
|
||||
disabled={disabled || selectedNodeIsRoot}
|
||||
property="positionType"
|
||||
value={node ? node.positionType : undefined}
|
||||
onChange={this.props.onChangeLayout}
|
||||
/>
|
||||
<EditValue
|
||||
disabled={selectedNodeIsRoot}
|
||||
property="position"
|
||||
value={node ? node.position : undefined}
|
||||
onChange={this.props.onChangeLayout}
|
||||
|
@@ -26,11 +26,10 @@ export default class InfoText extends Component<Props> {
|
||||
<Popover
|
||||
content={
|
||||
<div className="InfoText">
|
||||
{
|
||||
[
|
||||
<p>{[...this.props.children]}</p>,
|
||||
<Link className="docs-link" to={this.props.doclink}>DOCUMENTATION</Link>]
|
||||
}
|
||||
<p>{this.props.children}</p>
|
||||
<Link className="docs-link" to={this.props.doclink}>
|
||||
DOCUMENTATION
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
trigger="hover">
|
||||
|
@@ -21,7 +21,7 @@ import type {
|
||||
Yoga$JustifyContent,
|
||||
Yoga$FlexDirection,
|
||||
Yoga$FlexWrap,
|
||||
Yoga$Yoga$PositionType,
|
||||
Yoga$PositionType,
|
||||
} from 'yoga-layout';
|
||||
|
||||
export type LayoutRecordT = RecordOf<{
|
||||
@@ -32,17 +32,18 @@ export type LayoutRecordT = RecordOf<{
|
||||
border: PositionRecordT,
|
||||
margin: PositionRecordT,
|
||||
position: PositionRecordT,
|
||||
positionType: Yoga$Yoga$PositionType,
|
||||
positionType: Yoga$PositionType,
|
||||
alignItems?: Yoga$Align,
|
||||
alignSelf?: Yoga$Align,
|
||||
alignContent?: Yoga$Align,
|
||||
flexDirection?: Yoga$FlexDirection,
|
||||
flexBasis?: number | 'auto',
|
||||
flexGrow?: number,
|
||||
flexShrink?: number,
|
||||
padding?: number,
|
||||
flexWrap?: Yoga$FlexWrap,
|
||||
aspectRatio?: number,
|
||||
children?: List<LayoutDefinition>,
|
||||
children?: List<LayoutRecordT>,
|
||||
}>;
|
||||
|
||||
const r: LayoutRecordT = Record({
|
||||
@@ -59,6 +60,7 @@ const r: LayoutRecordT = Record({
|
||||
position: PositionRecord(),
|
||||
positionType: yoga.POSITION_TYPE_RELATIVE,
|
||||
flexWrap: yoga.WRAP_NO_WRAP,
|
||||
flexBasis: 'auto',
|
||||
flexGrow: 0,
|
||||
flexShrink: 1,
|
||||
children: List(),
|
||||
|
@@ -12,50 +12,13 @@
|
||||
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;
|
||||
}
|
||||
|
@@ -11,29 +11,17 @@
|
||||
*/
|
||||
|
||||
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>
|
||||
)}
|
||||
<div className="Sidebar" style={{width: this.props.width}}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
|
@@ -1,7 +1,2 @@
|
||||
.URLShortener {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 30px;
|
||||
z-index: 4;
|
||||
box-shadow: 1px 1px 15px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
@@ -16,10 +16,37 @@ import './URLShortener.css';
|
||||
|
||||
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;
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
shortURL: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener('hashchange', this.onHashChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('hashchange', this.onHashChange);
|
||||
}
|
||||
|
||||
onHashChange = () => {
|
||||
this.setState({shortURL: null});
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
this.setState(
|
||||
{
|
||||
loading: true,
|
||||
},
|
||||
() => {
|
||||
fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${API_KEY}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -32,12 +59,16 @@ export default class URLShortener extends Component<{}> {
|
||||
}),
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(({id}) => {
|
||||
notification.open({
|
||||
message: 'Created short URL',
|
||||
description: (
|
||||
.then(({id}) => this.setState({shortURL: id, loading: false}))
|
||||
.catch(() => this.setState({shortURL: null, loading: false}));
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return this.state.shortURL ? (
|
||||
<Input
|
||||
value={id}
|
||||
value={this.state.shortURL}
|
||||
autoFocus
|
||||
ref={ref => {
|
||||
if (ref) {
|
||||
@@ -45,24 +76,15 @@ export default class URLShortener extends Component<{}> {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
),
|
||||
placement: 'bottomLeft',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Tooltip title="Copy URL">
|
||||
) : (
|
||||
<Button
|
||||
className="URLShortener"
|
||||
onClick={this.onClick}
|
||||
icon="share-alt"
|
||||
type="primary"
|
||||
shape="circle"
|
||||
size="large"
|
||||
/>
|
||||
</Tooltip>
|
||||
disabled={this.state.loading}
|
||||
type="primary">
|
||||
Share URL
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ type Property = 'position' | 'margin' | 'padding' | 'border';
|
||||
type Props = {
|
||||
value: PositionRecordT,
|
||||
property: Property,
|
||||
disabled?: boolean,
|
||||
onChange: (property: Property, value: PositionRecordT) => void,
|
||||
};
|
||||
|
||||
@@ -32,18 +33,22 @@ export default class YogaPositionEditor extends Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {onChange, value, property} = this.props;
|
||||
const {onChange, value, property, disabled} = this.props;
|
||||
return (
|
||||
<div className="YogaPositionEditor">
|
||||
<Input
|
||||
type="text"
|
||||
value={value.top}
|
||||
disabled={disabled}
|
||||
placeholder="0"
|
||||
onChange={e => onChange(property, value.set('top', e.target.value))}
|
||||
/>
|
||||
<div className="YogaPositionEditorRow">
|
||||
<Input
|
||||
type="text"
|
||||
value={value.left}
|
||||
disabled={disabled}
|
||||
placeholder="0"
|
||||
onChange={e =>
|
||||
onChange(property, value.set('left', e.target.value))
|
||||
}
|
||||
@@ -52,6 +57,8 @@ export default class YogaPositionEditor extends Component<Props> {
|
||||
<Input
|
||||
type="text"
|
||||
value={value.right}
|
||||
disabled={disabled}
|
||||
placeholder="0"
|
||||
onChange={e =>
|
||||
onChange(property, value.set('right', e.target.value))
|
||||
}
|
||||
@@ -60,6 +67,8 @@ export default class YogaPositionEditor extends Component<Props> {
|
||||
<Input
|
||||
type="text"
|
||||
value={value.bottom}
|
||||
disabled={disabled}
|
||||
placeholder="0"
|
||||
onChange={e =>
|
||||
onChange(property, value.set('bottom', e.target.value))
|
||||
}
|
||||
|
@@ -43,9 +43,30 @@
|
||||
100px 100px, 100px 100px, 100px 100px;
|
||||
}
|
||||
|
||||
|
||||
.Playground > .YogaNode {
|
||||
margin: auto;
|
||||
position: static;
|
||||
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 yoga from 'yoga-layout/dist/entry-browser';
|
||||
import YogaNode from './YogaNode';
|
||||
import CodeGenerators from './CodeGenerators';
|
||||
import URLShortener from './URLShortener';
|
||||
import Editor from './Editor';
|
||||
import {List, setIn} from 'immutable';
|
||||
import PositionRecord from './PositionRecord';
|
||||
import LayoutRecord from './LayoutRecord';
|
||||
import URLShortener from './URLShortener';
|
||||
import Code from './Code';
|
||||
import Sidebar from './Sidebar';
|
||||
import {Row, Col} from 'antd';
|
||||
import type {LayoutRecordT} from './LayoutRecord';
|
||||
import type {Yoga$Direction} from 'yoga-layout';
|
||||
import './index.css';
|
||||
@@ -42,7 +43,6 @@ type State = {
|
||||
selectedNodePath: ?Array<number>,
|
||||
layoutDefinition: LayoutRecordT,
|
||||
direction: Yoga$Direction,
|
||||
showCode: boolean,
|
||||
};
|
||||
|
||||
function getPath(path: Array<number>): Array<mixed> {
|
||||
@@ -60,7 +60,6 @@ export default class Playground extends Component<Props, State> {
|
||||
},
|
||||
direction: yoga.DIRECTION_LTR,
|
||||
maxDepth: 3,
|
||||
showCode: false,
|
||||
showGuides: true,
|
||||
persist: false,
|
||||
};
|
||||
@@ -79,7 +78,6 @@ export default class Playground extends Component<Props, State> {
|
||||
selectedNodePath: this.props.selectedNodePath,
|
||||
layoutDefinition: this.rehydrate(this.props.layoutDefinition),
|
||||
direction: this.props.direction,
|
||||
showCode: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -118,7 +116,6 @@ export default class Playground extends Component<Props, State> {
|
||||
// sidebar may rely on a certain node to be selected
|
||||
this.setState({
|
||||
selectedNodePath: null,
|
||||
showCode: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -211,17 +208,8 @@ export default class Playground extends Component<Props, State> {
|
||||
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 {layoutDefinition, selectedNodePath, direction} = this.state;
|
||||
const {height} = this.props;
|
||||
|
||||
const selectedNode: ?LayoutRecordT = selectedNodePath
|
||||
@@ -239,20 +227,26 @@ export default class Playground extends Component<Props, State> {
|
||||
<YogaNode
|
||||
layoutDefinition={layoutDefinition}
|
||||
selectedNodePath={selectedNodePath}
|
||||
onClick={selectedNodePath =>
|
||||
this.setState({selectedNodePath, showCode: false})
|
||||
}
|
||||
onClick={selectedNodePath => this.setState({selectedNodePath})}
|
||||
onDoubleClick={this.onAdd}
|
||||
direction={this.state.direction}
|
||||
direction={direction}
|
||||
showGuides={this.props.showGuides}
|
||||
/>
|
||||
<Sidebar
|
||||
visible={
|
||||
Boolean(selectedNodePath) &&
|
||||
!this.state.showCode &&
|
||||
!this.props.renderSidebar
|
||||
}
|
||||
floating>
|
||||
<Sidebar>
|
||||
<div className="Actions">
|
||||
<Row gutter={15}>
|
||||
<Col span={12}>
|
||||
<CodeGenerators
|
||||
layoutDefinition={layoutDefinition}
|
||||
direction={direction}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<URLShortener />
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
{this.state.selectedNodePath ? (
|
||||
<Editor
|
||||
node={selectedNode}
|
||||
selectedNodeIsRoot={
|
||||
@@ -260,25 +254,25 @@ export default class Playground extends Component<Props, State> {
|
||||
}
|
||||
onChangeLayout={this.onChangeLayout}
|
||||
onChangeSetting={(key, value) => this.setState({[key]: value})}
|
||||
direction={this.state.direction}
|
||||
direction={direction}
|
||||
onRemove={
|
||||
selectedNodePath && selectedNodePath.length > 0
|
||||
? this.onRemove
|
||||
: undefined
|
||||
}
|
||||
onAdd={
|
||||
selectedNodePath && selectedNodePath.length < this.props.maxDepth
|
||||
selectedNodePath &&
|
||||
selectedNodePath.length < this.props.maxDepth
|
||||
? this.onAdd
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<div className="NoContent">
|
||||
Select a node to edit its properties
|
||||
</div>
|
||||
)}
|
||||
</Sidebar>
|
||||
<Sidebar
|
||||
width={500}
|
||||
visible={this.state.showCode}
|
||||
onClose={() => this.setState({showCode: false})}
|
||||
/>
|
||||
{this.props.persist && <URLShortener />}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user