Skip to content

Commit

Permalink
Merge pull request #39 from jesstelford/react-ui-#5
Browse files Browse the repository at this point in the history
React ui #5
  • Loading branch information
jesstelford committed Mar 9, 2016
2 parents c6975fa + c13d666 commit 622ac25
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 273 deletions.
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"build-html": "(cd src; tar -cf - $(find ./html -name \"*.html\")) | tar -C lib -xf - && cp src/devtools.html lib/devtools.html",
"build-manifest": "node -e \"var manifest = require('./src/manifest.json'); manifest.version = require('./package.json').version; console.log(JSON.stringify(manifest, null, 2));\" > lib/manifest.json",
"build-static": "npm run build-gfx && npm run build-css && npm run build-html && npm run build-manifest",
"build-js": "find ./src/js -maxdepth 1 -name '*.js' -exec bash -c 'browserify \"$0\" -t [ babelify --presets [ es2015 ] ] -g [ envify --NO_WRITE_FS --NODE_ENV=\"development\" ] -g uglifyify -o \"${0/src/lib}\"' {} \\;",
"build-js-prod": "find ./src/js -maxdepth 1 -name '*.js' -exec bash -c 'browserify \"$0\" -t [ babelify --presets [ es2015 ] ] -g [ envify --NO_WRITE_FS --GA_TRACKING_ID=\"UA-73151166-1\" --NODE_ENV=\"production\" ] -g uglifyify | uglifyjs -cm --screw-ie8 -o \"${0/src/lib}\"' {} \\;"
"build-js": "find ./src/js -maxdepth 1 -name '*.js' -exec bash -c 'browserify \"$0\" -t [ babelify --presets [ es2015 react ] ] -g [ envify --NO_WRITE_FS --NODE_ENV=\"development\" ] -g uglifyify -o \"${0/src/lib}\"' {} \\;",
"build-js-prod": "find ./src/js -maxdepth 1 -name '*.js' -exec bash -c 'browserify \"$0\" -t [ babelify --presets [ es2015 react ] ] -g [ envify --NO_WRITE_FS --GA_TRACKING_ID=\"UA-73151166-1\" --NODE_ENV=\"production\" ] -g uglifyify | uglifyjs -cm --screw-ie8 -o \"${0/src/lib}\"' {} \\;"
},
"repository": {
"type": "git",
Expand All @@ -28,12 +28,14 @@
"homepage": "",
"dependencies": {
"babelify": "^7.2.0",
"he": "^0.5.0",
"html-to-react-components": "github:roman01la/html-to-react-components#c65387c3ef9b9a973ca6ab7800c6302d302038e1",
"lodash": "^4.6.1"
"lodash": "^4.6.1",
"react": "^0.14.7",
"react-dom": "^0.14.7"
},
"devDependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.5.0",
"browserify": "^13.0.0",
"envify": "^3.4.0",
"uglify-js": "^2.6.1",
Expand Down
52 changes: 1 addition & 51 deletions src/html/panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,57 +12,7 @@

<div class="container">
<h1>Extract To React</h1>
<p>
Inspected Element: <span id='inspected'><i>none</i></span>
</p>
<p>Generate and upload to...</p>
<button id="codepen" class="extract-button">Codepen</button>
<button id="jsfiddle" class="extract-button">JSFiddle</button>
<section id="usage">
<h2>Usage</h2>
<h3>Selecting what to extract</h3>
<p>Inspect an element on the page, then re-visit this tab.</p>
<p>To inspect an element;
<ul>
<li>Right click > Inspect Element, or;</li>
<li>Open "Elements" panel > Click an element, or;</li>
<li>Click the magnifying glass, then select an element on the page</li>
</ul>
</section>
<section id='advanced-usage'>
<h2>Advanced Usage</h2>
<h3>Extracting multiple nested components</h3>
<p>Ordinarily, only a single component is extracted. It is possible to split this monolithic component up into multiple nested components:</p>
<ul>
<li>Find the elements you wish to become their own component in the "Elements" tab</li>
<li>Add an attribute called <code>data-component</code>.
<li>Set the value of <code>data-component</code> to be the name of the component.</li>
</ul>
<p>For example:
<pre><code>
&#x3C;h1 class=&#x22;heading&#x22; data-component=&#x22;Heading&#x22;&#x3E;Hello, world!&#x3C;/h1&#x3E;

&#x3C;nav class=&#x22;nav&#x22; data-component=&#x22;Nav&#x22;&#x3E;
&#x3C;ul class=&#x22;list&#x22;&#x3E;
&#x3C;li class=&#x22;list-item&#x22; data-component=&#x22;ListItem&#x22;&#x3E;#1&#x3C;/li&#x3E;
&#x3C;li class=&#x22;list-item&#x22; data-component=&#x22;ListItem&#x22;&#x3E;#2&#x3C;/li&#x3E;
&#x3C;/ul&#x3E;
&#x3C;/nav&#x3E;
</code></pre>
Will result in 3 components being extracted: <code>Heading</code>, <code>Nav</code>, and <code>ListItem</code>
</p>
</section>
<hr />
<section>
<p>Created by <a href="http://github.com/jesstelford" target="_blank">Jess Telford</a> | <a href="https://github.com/jesstelford/extract-to-react" target="_blank">Homepage</a></p>
<p>
Built on the shoulders of giants:
<ul>
<li><a href="https://github.com/roman01la/html-to-react-components" target="_blank"><code>html-to-react-components</code></a> by <a href="https://github.com/roman01la" target="_blank">Roman Liutikov</a></li>
<li><a href="https://github.com/kdzwinel/SnappySnippet" target="_blank">SnappySnippet</a> by <a href="https://github.com/kdzwinel" target="_blank">Konrad Dzwinel</a></li>
</ul>
</p>
</section>
<div class="panel-component"></div>
</div>

<script src='../js/panel.js'></script>
Expand Down
29 changes: 29 additions & 0 deletions src/js/components/advanced-usage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';

export default function AdvancedUsage() {
return (
<section id='advanced-usage'>
<h2>Advanced Usage</h2>
<h3>Extracting multiple nested components</h3>
<p>Ordinarily, only a single component is extracted. It is possible to split this monolithic component up into multiple nested components:</p>
<ul>
<li>Find the elements you wish to become their own component in the "Elements" tab</li>
<li>Add an attribute called <code>data-component</code>.</li>
<li>Set the value of <code>data-component</code> to be the name of the component.</li>
</ul>
<p>For example:</p>
<pre><code>
&#x3C;h1 class=&#x22;heading&#x22; data-component=&#x22;Heading&#x22;&#x3E;Hello, world!&#x3C;/h1&#x3E;

&#x3C;nav class=&#x22;nav&#x22; data-component=&#x22;Nav&#x22;&#x3E;
&#x3C;ul class=&#x22;list&#x22;&#x3E;
&#x3C;li class=&#x22;list-item&#x22; data-component=&#x22;ListItem&#x22;&#x3E;#1&#x3C;/li&#x3E;
&#x3C;li class=&#x22;list-item&#x22; data-component=&#x22;ListItem&#x22;&#x3E;#2&#x3C;/li&#x3E;
&#x3C;/ul&#x3E;
&#x3C;/nav&#x3E;
</code></pre>
<p>Will result in 3 components being extracted: <code>Heading</code>, <code>Nav</code>, and <code>ListItem</code>
</p>
</section>
);
}
271 changes: 271 additions & 0 deletions src/js/components/extractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
import React from 'react';
import Usage from './usage';
import Footer from './footer';
import AdvancedUsage from './advanced-usage';
import prettyPrintHtml from '../tools/pretty-print-html';

var ga = require('../analytics'),
packageJson = require('../../../package.json'),
makeSnapshot = require('../tools/make-snapshot'),
convertToReact = require('../tools/convert-to-react');

/**
* @param name String name to send to GA
* @param button A DOMNode to add a click listener to
* @param loadingText [Optional] A string to insert inside the HTML
* @param buildPostData A function to construct POST data for submitting a
* form
*/
function linkTrigger(name, loadingText, buildPostData) {

var startTime;

// loadingText is optional
if (typeof loadingText === 'function' && typeof buildPostData === 'undefined') {
buildPostData = loadingText;
loadingText = '';
}

ga(
'send',
'event',
'link',
'click',
name,
{
'nonInteraction': true // Don't count against bounce rate
}
);

startTime = performance.now();

makeSnapshot(function(error, output) {

var processingTime = Math.round(performance.now() - startTime);

var bugUrl = packageJson.bugs.url + '/new',
errorTitle = encodeURIComponent('Error after extracting'),
errorBody;

if (error) {
let errorMessage = error.toString() + '\n' + error.stack;
chrome.runtime.sendMessage({type: 'error', message: errorMessage});

ga(
'send',
'exception',
{
'exDescription': errorMessage,
'exFatal': false
}
);

return;
}

ga(
'send',
'timing',
{
'timingCategory': 'processing',
'timingVar': 'extracting-complete',
'timingValue': processingTime,
'timingLabel': 'Extracting DOM Complete'
}
);

var {html: originalHtml, css: originalCss, url: originalUrl} = output;

startTime = performance.now();

output = convertToReact(output, loadingText);

processingTime = Math.round(performance.now() - startTime);

ga(
'send',
'timing',
{
'timingCategory': 'processing',
'timingVar': 'convert-to-react-complete',
'timingValue': processingTime,
'timingLabel': 'Convert To React Complete'
}
);

errorBody = encodeURIComponent(buildErrorReport(originalHtml, originalCss, originalUrl));

output.html = output.html + '\n\n' + generateBugButton(bugUrl + '?title=' + errorTitle + '&body=' + errorBody);

chrome.runtime.sendMessage({
post: buildPostData(output)
});

});

}

// Progressively build the error report, keeping it under the `maxLength`
function buildErrorReport(html, css, url, maxLength = 2000) {

var htmlOut,
cssOut,
error;

error = `**Error**:
\`\`\`
<TODO: Fill in your error>
\`\`\`
**Version**: v${packageJson.version}
**URL**: ${url}`;

htmlOut = `
**Extracting**:
\`\`\`html
${html}
\`\`\``;

if (error.length + htmlOut.length > maxLength) {
return error;
} else {
error = error + htmlOut;
}

cssOut = `
\`\`\`css
${css}
\`\`\``;

if (error.length + cssOut.length > maxLength) {
return error;
} else {
error = error + cssOut
}

return error;
}

function generateBugButton(url) {
// TODO: global regex to replace ' with \' in the url string
return '<button type="button" onclick="window.open(\'' + url.replace(/'/g, "\\'") + '\', \'_blank\')" style="position:absolute; right: 20px; bottom: 20px;">Not working?</button>';
}


let Extractor = React.createClass({

propTypes: {
inspected: React.PropTypes.shape({
url: React.PropTypes.string,
html: React.PropTypes.string,
css: React.PropTypes.string
}).isRequired
},

prepareForRender(html) {
// TODO: html-entities the html, then return an array of elements to render
return prettyPrintHtml(html).join('<br />');
},

getInitialState() {
return {
hasInspected: !!this.props.inspected.html,
prettyInspected: this.prepareForRender(this.props.inspected.html)
}
},

componentWillReceiveProps(newProps) {
if (
newProps.inspected.url !== this.props.inspected.url
|| newProps.inspected.html !== this.props.inspected.html
|| newProps.inspected.css !== this.props.inspected.css
) {
this.setState({
hasInspected: !!newProps.inspected.html,
prettyInspected: this.prepareForRender(newProps.inspected.html)
});
}
},

handleCodepen(event) {
event.stopPropagation();
event.preventDefault();

linkTrigger('codepen', function(output) {

return {
url: 'http://codepen.io/pen/define',
data: {
"data": JSON.stringify({
html: output.html,
css: output.css,
js: output.js,
js_external: 'https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.min.js;https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.min.js',
js_pre_processor: 'babel'
})
}
}
});

},

handleJsfiddle() {
event.stopPropagation();
event.preventDefault();

// Note we give instructions to click Run because JSFiddle doesn't correctly
// execute the JSX post-babel transform on the first (page load) run.
// Subsequent runs work fine.
linkTrigger('jsfiddle', 'Click <i>Run</i> to see results', function(output) {

// Code required to trigger babel conversion of JSX
var html = output.html
+ '\n\n'
+ '<!-- Trigger babel conversion of JSX -->\n'
+ '<script>document.querySelector(\'script[type="application/javascript;version=1.7"]\').setAttribute(\'type\', \'text/babel\');</script>';

return {
url: 'http://jsfiddle.net/api/post/library/pure/',
data: {
html: html,
css: output.css,
js: output.js,
resources: 'https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.js,https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.min.js,https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.min.js',
wrap: 'h',// No wrap, in the head
panel_js: 2 // version 1.7
}
}
});
},

render() {

let inspectedContent = this.state.prettyInspected,
buttonProps = {};

if (!this.state.hasInspected) {
buttonProps.disabled = true;
inspectedContent = <i>none</i>;
}

return (
<div>
<p>Inspected Element:</p>
<pre>
<code>
{inspectedContent}
</code>
</pre>
<p>Generate and upload to...</p>
<button {...buttonProps} onClick={this.handleCodepen}>Codepen</button>
<button {...buttonProps} onClick={this.handleJsfiddle}>JSFiddle</button>
</div>
);
}
});

export default Extractor;
Loading

0 comments on commit 622ac25

Please sign in to comment.