├── .surgeignore
├── CNAME
├── example.png
├── .gitignore
├── .babelrc
├── lib
├── ToolTipButton
│ ├── style.js
│ └── index.js
├── Sandbox.js
├── utils.js
├── ReactPlay.js
├── Header.js
└── App.js
├── oauth_redirect.html
├── index.js
├── README.md
├── index.html
├── package.json
└── config.js
/.surgeignore:
--------------------------------------------------------------------------------
1 | jspm_packages/
2 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | react-play.surge.sh
2 |
--------------------------------------------------------------------------------
/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cem2ran/react-play/HEAD/example.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | node_modules/
4 | jspm_packages/
5 | bundle/*
6 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"],
3 | "plugins": ["syntax-object-rest-spread"]
4 | }
5 |
--------------------------------------------------------------------------------
/lib/ToolTipButton/style.js:
--------------------------------------------------------------------------------
1 | export default {
2 | border: 0,
3 | padding: '8px',
4 | background: 'none',
5 | fontWeight: 300,
6 | cursor: 'pointer',
7 | float: 'left'
8 | }
9 |
--------------------------------------------------------------------------------
/oauth_redirect.html:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/lib/Sandbox.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import srcDoc from 'srcdoc-polyfill';
3 |
4 | const Sandbox = ({src, html, sandbox, style={}}) => {
5 | sandbox = sandbox ? sandbox.join(' ') : ''
6 |
7 | return ;
11 | }
12 |
13 | export default Sandbox
14 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import { render } from 'react-dom';
3 |
4 | import App from './lib/App'
5 |
6 | render(, document.body.appendChild(document.createElement("react.play")));
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React Play
2 | ==
3 |
4 | A playground for React powered by [SystemJS](https://github.com/systemjs/systemjs), a universal module loader, capable of running in the browser and pull in dependencies from npm, github and more, through [jspm.io](http://jspm.io/).
5 |
6 | There are several ways to share your experiments. Simply copy the link, shorten it, or create a downloadable gist by logging in.
7 |
8 | ##[DEMO](https://goo.gl/DS1fHW)
9 | 
10 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
React Play
3 |
14 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/lib/ToolTipButton/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ToolTip from 'react-portal-tooltip'
3 | import style from './style.js'
4 |
5 | const ToolTipButton = ({id, buttonText, isShown, hide, onClick, children}) =>
6 |
7 |
8 | {children}
9 |
10 |
11 |
16 |
17 |
18 | export default ToolTipButton
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "jspm": {
3 | "dependencies": {
4 | "brace": "npm:brace@^0.7.0",
5 | "iframe": "npm:iframe@^0.3.1",
6 | "lodash": "npm:lodash@^3.10.1",
7 | "react": "npm:react@^0.14.5",
8 | "react-ace": "npm:react-ace@^3.0.0",
9 | "react-dimensions": "npm:react-dimensions@^1.0.1",
10 | "react-dom": "npm:react-dom@^0.14.5",
11 | "react-github-corner": "npm:react-github-corner@^0.1.0",
12 | "react-portal-tooltip": "npm:react-portal-tooltip@^1.0.0",
13 | "srcdoc-polyfill": "npm:srcdoc-polyfill@^0.2.0",
14 | "whatwg-fetch": "npm:whatwg-fetch@^0.10.1"
15 | },
16 | "devDependencies": {
17 | "babel": "npm:babel-core@^5.8.24",
18 | "babel-runtime": "npm:babel-runtime@^5.8.24",
19 | "core-js": "npm:core-js@^1.1.4"
20 | }
21 | },
22 | "name": "react-play",
23 | "version": "1.0.0",
24 | "description": "A React.js playground powered by JSPM/SystemJS.",
25 | "main": "index.js",
26 | "scripts": {
27 | "depcache": "jspm setmode remote && jspm depcache index.js && surge . && jspm setmode local",
28 | "bundle": "jspm bundle index.js bundle.js --inject --minify && cp system.js system.js.map CNAME config.js index.html oauth_redirect.html bundle/ && mv bundle.js bundle.js.map bundle/ && jspm unbundle && surge bundle/",
29 | "postinstall": "jspm install && cp jspm_packages/system.js jspm_packages/system.js.map .",
30 | "test": "echo \"Error: no test specified\" && exit 1"
31 | },
32 | "repository": {
33 | "type": "git",
34 | "url": "git+https://github.com/cem2ran/react-play.git"
35 | },
36 | "author": "cem2ran",
37 | "license": "MIT",
38 | "bugs": {
39 | "url": "https://github.com/cem2ran/react-play/issues"
40 | },
41 | "homepage": "https://github.com/cem2ran/react-play#readme",
42 | "devDependencies": {
43 | "jspm": "^0.16.19"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 | export const encode = (s) => btoa(unescape(encodeURIComponent(s)).replace(/[\u00A0-\u2666]/g, c => '' + c.charCodeAt(0) + ';'))
2 |
3 | export const decode = (s) => decodeURIComponent(escape(atob(s)))
4 |
5 | import 'whatwg-fetch'
6 | export const createShortURL = (URL_SHORTENER_API_KEY) => (URL) =>
7 | fetch(`https://www.googleapis.com/urlshortener/v1/url?key=${URL_SHORTENER_API_KEY}`, {
8 | method: 'post',
9 | headers: {
10 | 'Accept': 'application/json',
11 | 'Content-Type': 'application/json'
12 | },
13 | body: JSON.stringify({
14 | longUrl: URL
15 | })
16 | }).then(json)
17 |
18 | export const authenticate = (token) => fetch(`https://api.github.com/user?access_token=${token}`).then(json)
19 |
20 | export const requestToken = (endpoint, code) => fetch(endpoint + code).then(json)
21 |
22 | export const requestGistToken = (app) => (username, password) =>
23 | fetch('https://api.github.com/authorizations',{
24 | method: 'post',
25 | headers: {
26 | 'Accept': 'application/json',
27 | 'Content-Type': 'application/json',
28 | "Authorization": "Basic " + btoa(`${username}:${password}`)
29 | },
30 | body: JSON.stringify({"scopes":["gist"],"note" : app})
31 | }).then(json)
32 |
33 | export const fetchUser = (login) =>
34 | fetch(`https://api.github.com/users/${login}`)
35 | .then(json)
36 |
37 | export const updateGist = (token) => ({description, hidden=false, files, id}) =>{
38 | console.log(description, hidden, files, id)
39 | return fetch(`https://api.github.com/gists` + (id ? `/${id}` : ''), {
40 | method: (id ? 'PATCH' : 'POST'),
41 | headers: {
42 | 'Accept': 'application/json',
43 | 'Content-Type': 'application/json',
44 | "Authorization": "token " + token
45 | },
46 | body: JSON.stringify({
47 | description, 'public': !hidden ,files
48 | })
49 | }).then(json)
50 | }
51 |
52 |
53 | function json(response) {
54 | return response.json()
55 | }
56 |
--------------------------------------------------------------------------------
/lib/ReactPlay.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 | import { render } from 'react-dom';
3 | import brace from 'brace';
4 | import AceEditor from 'react-ace';
5 | import Dimensions from 'react-dimensions' //used to adjust height of ace editor...
6 |
7 | import 'brace/mode/javascript';
8 | import 'brace/theme/chrome';
9 |
10 | import {debounce} from 'lodash'
11 |
12 | import Sandbox from './Sandbox'
13 |
14 | //TODO - refactor: use htmlScaffold
15 | const sfxPayload = (origin, payload) =>
16 | !payload
17 | ? ''
18 | : [''].join(' ')
22 |
23 | const ReactPlay = ({editorCode, sandboxCode, updateCode, error, containerHeight}) =>
24 |
25 |
26 |
updateCode(text), 750)}
33 | name="react_play_editor"
34 | onLoad={editor => editor.getSession().setUseWorker(false)}
35 | editorProps={{
36 | $blockScrolling: true,
37 | style:{ position: 'absolute' }
38 | }}
39 | />
40 |
41 |
42 | {
43 | error
44 | ?
45 | :
48 | }
49 |
50 |
51 |
52 | function Error({stacktrace}) {
53 | const error = stacktrace.split(":")
54 | const name = error[0];
55 | const messages = error.slice(1).join(":").split("|");
56 | const stack = messages.pop();
57 |
58 | return
59 | {name}
60 | {messages.join("|").split(":").slice(1).join(":")}
61 | {stack}
62 |
63 | }
64 |
65 | export default Dimensions()(ReactPlay)
66 |
--------------------------------------------------------------------------------
/lib/Header.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 |
3 | import ToolTipButton from './ToolTipButton/index'
4 | import ToolTipButtonStyle from './ToolTipButton/style'
5 |
6 | import GithubCorner from 'react-github-corner'
7 |
8 | import {createShortURL, requestGistToken, fetchUser, requestToken, authenticate} from './utils'
9 |
10 | const USER_STORAGE_KEY = 'react.play.user'
11 |
12 | const gistUrl = (user, id) => `https://gist.github.com/${user}/${id}`
13 |
14 | export default class Header extends Component {
15 |
16 | static propTypes = {
17 | URL_SHORTENER_API_KEY: PropTypes.string,
18 | GITHUB_CLIENT_ID: PropTypes.string,
19 | GITHUB_TOKEN_ENDPOINT: PropTypes.string
20 | }
21 |
22 | constructor(props){
23 | super(props)
24 |
25 | const {user, token} = JSON.parse(localStorage.getItem(USER_STORAGE_KEY)) || {}
26 |
27 | this.state = {
28 | shortUrl: '',
29 | showTooltip: false,
30 | showAuthTooltip: false,
31 | user, token,
32 | username: null, password: null,
33 | description: null
34 | }
35 | window.addEventListener('message', (event) =>{
36 | if(event.data.type === 'react.play.token') {
37 | requestToken(props.GITHUB_TOKEN_ENDPOINT, event.data.payload).then(({token}) => {
38 | authenticate(token).then(user => {
39 | localStorage.setItem(USER_STORAGE_KEY, JSON.stringify({user, token}))
40 | this.setState({
41 | user, token
42 | })
43 | })
44 | })
45 | }
46 | });
47 | //get and set gist details if exists
48 | }
49 |
50 | hideTooltip(key){
51 | if(this.state[key])
52 | this.setState({[key]: false})
53 | }
54 |
55 | getShortURL(){
56 | createShortURL(this.props.URL_SHORTENER_API_KEY)(location.href)
57 | .then(({id, err}) => {
58 | if(id){
59 | this.setState({
60 | showTooltip: true,
61 | shortUrl: id
62 | })
63 | }
64 | })
65 | }
66 |
67 | handleInput(key, event){
68 | console.log(key, event)
69 | this.setState({[key]: event.target.value})
70 | }
71 |
72 | handleLoginClick(){
73 | window.open(`https://github.com/login/oauth/authorize?client_id=${this.props.GITHUB_CLIENT_ID}&scope=gist`)
74 | }
75 |
76 | render(){
77 | return
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react';
2 |
3 | import Header from './Header'
4 | import ReactPlay from './ReactPlay'
5 | import {updateGist, encode, decode} from './utils'
6 |
7 | const DEFAULT_STORAGE_KEY = 'react.play'
8 |
9 | const DEFAULT_HTML_FILENAME = 'react.play.html'
10 | const DEFAULT_JS_FILENAME = 'index.js'
11 |
12 | const htmlScaffold = (filename, body='') => `${body}