From 43fda26275b6ee15aa41549b1fc7e538b4e37871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=BCchele?= Date: Wed, 14 Feb 2018 10:51:57 -0800 Subject: [PATCH] add share and code buttons Summary: Adds code and share buttons to the editor Reviewed By: emilsjolander Differential Revision: D6989097 fbshipit-source-id: 67478fe0810a0af43524f24458c520acf2999219 --- website/src/components/Playground/Code.css | 29 ----- website/src/components/Playground/Code.js | 114 ------------------ .../components/Playground/CodeGenerators.js | 110 +++++++++++++++++ website/src/components/Playground/Editor.css | 1 + website/src/components/Playground/Editor.js | 78 +++++++++--- website/src/components/Playground/InfoText.js | 9 +- .../src/components/Playground/LayoutRecord.js | 8 +- website/src/components/Playground/Sidebar.css | 37 ------ website/src/components/Playground/Sidebar.js | 14 +-- .../components/Playground/URLShortener.css | 5 - .../src/components/Playground/URLShortener.js | 102 ++++++++++------ .../Playground/YogaPositionEditor.js | 11 +- website/src/components/Playground/index.css | 25 +++- website/src/components/Playground/index.js | 98 +++++++-------- 14 files changed, 321 insertions(+), 320 deletions(-) delete mode 100644 website/src/components/Playground/Code.css delete mode 100644 website/src/components/Playground/Code.js create mode 100644 website/src/components/Playground/CodeGenerators.js diff --git a/website/src/components/Playground/Code.css b/website/src/components/Playground/Code.css deleted file mode 100644 index aa5141e7..00000000 --- a/website/src/components/Playground/Code.css +++ /dev/null @@ -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; -} diff --git a/website/src/components/Playground/Code.js b/website/src/components/Playground/Code.js deleted file mode 100644 index 6c05b992..00000000 --- a/website/src/components/Playground/Code.js +++ /dev/null @@ -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, -}; - -type State = { - language: Language, -}; - -export default class Code extends Component { - 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 ( -
- this.setState({language: e.target.value})} - value={this.state.language}> - {this.props.languages.map(lang => ( - - {lang} - - ))} - -
- - {this.generateCode(this.state.language)} - -
-
- ); - } -} diff --git a/website/src/components/Playground/CodeGenerators.js b/website/src/components/Playground/CodeGenerators.js new file mode 100644 index 00000000..338e5ce0 --- /dev/null +++ b/website/src/components/Playground/CodeGenerators.js @@ -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 { + state = { + showModal: null, + }; + + onClick = ({key}: {key: string}) => this.setState({showModal: key}); + + render() { + const {showModal} = this.state; + + const menu = ( + + {Object.keys(LANGUAGES).map(key => ( + {LANGUAGES[key].title} + ))} + + ); + + return [ + this.setState({showModal: null})}> + {showModal && ( + + {LANGUAGES[showModal].generator( + this.props.layoutDefinition, + this.props.direction, + )} + + )} + , + + + , + ]; + } +} diff --git a/website/src/components/Playground/Editor.css b/website/src/components/Playground/Editor.css index ebf930aa..5b54be30 100644 --- a/website/src/components/Playground/Editor.css +++ b/website/src/components/Playground/Editor.css @@ -11,6 +11,7 @@ display: flex; flex-direction: column; height: 100%; + border-top: 1px solid #E8E8E8; } .Editor .field { diff --git a/website/src/components/Playground/Editor.js b/website/src/components/Playground/Editor.js index b26b50bc..e13cc9ac 100644 --- a/website/src/components/Playground/Editor.js +++ b/website/src/components/Playground/Editor.js @@ -58,7 +58,9 @@ export default class Editor extends Component {

Direction - Defines the direction of which text and items are laid out + + Defines the direction of which text and items are laid out +

{ />

Flex direction - Defines the direction of the main-axis + + Defines the direction of the main-axis +

{ /> - +

- Flex grow - The factor of remaining space should be given to this node + Basis + + The factor of remaining space should be given to this node + +

+ + + +

+ Grow + + The factor of remaining space should be given to this node +

{ onChange={this.props.onChangeLayout} /> - +

- Flex shrink - The shrink factor of this element if parent has no space left + Shrink + + The shrink factor of this element if parent has no space + left +

{

Flex wrap - Wrapping behaviour when child nodes don't fit into a single line + + Wrapping behaviour when child nodes don't fit into a single line +

{

Justify content - Aligns child nodes along the main-axis + + Aligns child nodes along the main-axis +

{

Align items - Aligns child nodes along the cross-axis + + Aligns child nodes along the cross-axis +

{

Align self - Override align items of parent + + Override align items of parent +

{

Align content - Alignment of lines along the cross-axis when wrapping + + Alignment of lines along the cross-axis when wrapping +

{

Width × height - Dimensions of the node + + Dimensions of the node +

{ {

Aspect ratio - Width/Height aspect ratio of node + + Width/Height aspect ratio of node +

{ key={property} value={node ? node[property] : undefined} onChange={this.props.onChangeLayout} + disabled={property === 'margin' && selectedNodeIsRoot} /> ))}

@@ -221,12 +260,13 @@ export default class Editor extends Component {

{ - { - [ -

{[...this.props.children]}

, - DOCUMENTATION] - } +

{this.props.children}

+ + DOCUMENTATION + } trigger="hover"> diff --git a/website/src/components/Playground/LayoutRecord.js b/website/src/components/Playground/LayoutRecord.js index 14ee2f95..7822ca6b 100644 --- a/website/src/components/Playground/LayoutRecord.js +++ b/website/src/components/Playground/LayoutRecord.js @@ -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, + children?: List, }>; 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(), diff --git a/website/src/components/Playground/Sidebar.css b/website/src/components/Playground/Sidebar.css index c6594608..184fe996 100644 --- a/website/src/components/Playground/Sidebar.css +++ b/website/src/components/Playground/Sidebar.css @@ -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; } diff --git a/website/src/components/Playground/Sidebar.js b/website/src/components/Playground/Sidebar.js index 1e0c622d..48f05099 100644 --- a/website/src/components/Playground/Sidebar.js +++ b/website/src/components/Playground/Sidebar.js @@ -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 { render() { return ( -
- {this.props.onClose && ( -
- -
- )} +
{this.props.children}
); diff --git a/website/src/components/Playground/URLShortener.css b/website/src/components/Playground/URLShortener.css index 2b9f5e12..e319fb2b 100644 --- a/website/src/components/Playground/URLShortener.css +++ b/website/src/components/Playground/URLShortener.css @@ -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); } diff --git a/website/src/components/Playground/URLShortener.js b/website/src/components/Playground/URLShortener.js index b93469b9..cf9aa61d 100644 --- a/website/src/components/Playground/URLShortener.js +++ b/website/src/components/Playground/URLShortener.js @@ -16,53 +16,75 @@ 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 = () => { - fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${API_KEY}`, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', + this.setState( + { + loading: true, }, - body: JSON.stringify({ - kind: 'urlshortener#url', - longUrl: window.location.href, - }), - }) - .then(res => res.json()) - .then(({id}) => { - notification.open({ - message: 'Created short URL', - description: ( - { - if (ref) { - ref.input.select(); - } - }} - /> - ), - placement: 'bottomLeft', - }); - }); + () => { + fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${API_KEY}`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + kind: 'urlshortener#url', + longUrl: window.location.href, + }), + }) + .then(res => res.json()) + .then(({id}) => this.setState({shortURL: id, loading: false})) + .catch(() => this.setState({shortURL: null, loading: false})); + }, + ); }; render() { - return ( - - ); } } diff --git a/website/src/components/Playground/YogaPositionEditor.js b/website/src/components/Playground/YogaPositionEditor.js index df1b1261..9e3cc8c2 100644 --- a/website/src/components/Playground/YogaPositionEditor.js +++ b/website/src/components/Playground/YogaPositionEditor.js @@ -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 { }; render() { - const {onChange, value, property} = this.props; + const {onChange, value, property, disabled} = this.props; return (
onChange(property, value.set('top', e.target.value))} />
onChange(property, value.set('left', e.target.value)) } @@ -52,6 +57,8 @@ export default class YogaPositionEditor extends Component { onChange(property, value.set('right', e.target.value)) } @@ -60,6 +67,8 @@ export default class YogaPositionEditor extends Component { onChange(property, value.set('bottom', e.target.value)) } diff --git a/website/src/components/Playground/index.css b/website/src/components/Playground/index.css index 8ff86f79..f492f54a 100644 --- a/website/src/components/Playground/index.css +++ b/website/src/components/Playground/index.css @@ -43,9 +43,30 @@ 100px 100px, 100px 100px, 100px 100px; } - .Playground > .YogaNode { margin: auto; position: static; align-self: center; -} \ No newline at end of file +} + +.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; +} diff --git a/website/src/components/Playground/index.js b/website/src/components/Playground/index.js index 2430f7bb..00f803c4 100644 --- a/website/src/components/Playground/index.js +++ b/website/src/components/Playground/index.js @@ -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, layoutDefinition: LayoutRecordT, direction: Yoga$Direction, - showCode: boolean, }; function getPath(path: Array): Array { @@ -60,7 +60,6 @@ export default class Playground extends Component { }, direction: yoga.DIRECTION_LTR, maxDepth: 3, - showCode: false, showGuides: true, persist: false, }; @@ -79,7 +78,6 @@ export default class Playground extends Component { 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 { // 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 { 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,46 +227,52 @@ export default class Playground extends Component { - this.setState({selectedNodePath, showCode: false}) - } + onClick={selectedNodePath => this.setState({selectedNodePath})} onDoubleClick={this.onAdd} - direction={this.state.direction} + direction={direction} showGuides={this.props.showGuides} /> - - 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 - } - /> + +
+ + + + + + + + +
+ {this.state.selectedNodePath ? ( + this.setState({[key]: value})} + direction={direction} + onRemove={ + selectedNodePath && selectedNodePath.length > 0 + ? this.onRemove + : undefined + } + onAdd={ + selectedNodePath && + selectedNodePath.length < this.props.maxDepth + ? this.onAdd + : undefined + } + /> + ) : ( +
+ Select a node to edit its properties +
+ )}
- this.setState({showCode: false})} - /> - {this.props.persist && }
);