├── stylesheets ├── print.css ├── github-dark.css └── stylesheet.css ├── .gitignore ├── images ├── hr.png ├── body-bg.png ├── zip-icon.png ├── highlight-bg.jpg ├── octocat-icon.png ├── tar-gz-icon.png └── CssToReact-logo.svg ├── jspm_packages └── github │ └── jspm │ ├── nodelibs-process@0.2.0-alpha.json │ └── nodelibs-process@0.2.0-alpha │ └── process.js ├── params.json ├── jspm.browser.js ├── LICENSE.md ├── Readme.md ├── src ├── transform.js └── app.jsx ├── package.json ├── index.html └── jspm.config.js /stylesheets/print.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stylesheets/github-dark.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | jspm_packages/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /images/hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/hr.png -------------------------------------------------------------------------------- /images/body-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/body-bg.png -------------------------------------------------------------------------------- /images/zip-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/zip-icon.png -------------------------------------------------------------------------------- /images/highlight-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/highlight-bg.jpg -------------------------------------------------------------------------------- /images/octocat-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/octocat-icon.png -------------------------------------------------------------------------------- /images/tar-gz-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/staxmanade/CssToReact/HEAD/images/tar-gz-icon.png -------------------------------------------------------------------------------- /jspm_packages/github/jspm/nodelibs-process@0.2.0-alpha.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "./process.js", 3 | "map": { 4 | "./process.js": { 5 | "node": "./process-node.js" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /params.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Convert Css to React Style JSON", 3 | "tagline": "Simple little tool to transform CSS into React style JSON", 4 | "body": "", 5 | "note": "Don't delete this file! It's used internally to help with page regeneration." 6 | } -------------------------------------------------------------------------------- /jspm.browser.js: -------------------------------------------------------------------------------- 1 | SystemJS.config({ 2 | baseURL: "/", 3 | production: true, 4 | paths: { 5 | "npm:react@15.0.1": "https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react.min.js", 6 | "npm:react-dom@15.0.1": "https://cdnjs.cloudflare.com/ajax/libs/react/15.0.1/react-dom.min.js", 7 | "github:": "./jspm_packages/github/", 8 | "npm:": "./jspm_packages/npm/", 9 | "app/": "./src/" 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # What is it? 4 | 5 | Allows you to paste plain CSS into a <textarea>, it will automatically transform that into something you can copy right into a React component inline style. 6 | 7 | # See it in action: [CssToReact](http://staxmanade.com/CssToReact/) 8 | 9 | # The story behind it... 10 | 11 | So you're working on a React app. It's up and running in you're favorite browser but you notice an issue with some layout. You think, ok, this should be easy to fix. You open up the developer tools, hack on some CSS within the browser till you get it looking just the way you want it to. Maybe it's several CSS properties you added or tweaked so you copy each of them into the clipboard so you can transfer them back to your application. 12 | 13 | Then you realize, these styles aren't coming from a CSS style sheet, they're in-line styles right in you're React component. 14 | 15 | Now you're like, FINE, I'll manually translate this to React-style-inline-CSS. This is fine if you do it once, but maybe you missed removing a dash and mis-cased a letter or maybe you forgot a comma, or left a semicolon in the converted style translation… Never happened to you? Oh, you are so amazing if only I was as cool as you. For myself and probably another 1 or 2 of you out there I hacked together a little tool that automates this translation. Maybe this should be a plugin to my text editor where I can right click and say "Paste as React Style" instead, but for now it's a single simple little web page that will automate the translation for you. 16 | 17 | So one night I was searching the React interwebs thinking someone __had__ to have build this tool already. Unfortunately I didn't find it anywhere but I DID find this handy little tool [raphamorim/native-css](https://github.com/raphamorim/native-css) which is a CLI version of what I wanted in the browser. So thanks to the quick bootstrap based on that tool, I was able to put this little project together in just a short evening... 18 | 19 | 20 | # Hack on this repo 21 | 22 | ``` 23 | npm install 24 | npm run dev 25 | ``` 26 | -------------------------------------------------------------------------------- /src/transform.js: -------------------------------------------------------------------------------- 1 | import cssParser from 'css'; 2 | 3 | // 4 | // Transform implementation or originally thanks to 5 | // https://github.com/raphamorim/native-css 6 | // 7 | 8 | function transformRules(self, rules, result) { 9 | rules.forEach(function (rule) { 10 | var obj = {}; 11 | if (rule.type === 'media') { 12 | var name = mediaNameGenerator(rule.media); 13 | var media = result[name] = result[name] || { 14 | "__expression__": rule.media 15 | }; 16 | transformRules(self, rule.rules, media) 17 | } else if (rule.type === 'rule') { 18 | rule.declarations.forEach(function (declaration) { 19 | if (declaration.type === 'declaration') { 20 | var cleanProperty = cleanPropertyName(declaration.property); 21 | obj[cleanProperty] = declaration.value; 22 | } 23 | }); 24 | rule.selectors.forEach(function (selector) { 25 | var name = nameGenerator(selector.trim()); 26 | result[name] = obj; 27 | }); 28 | } 29 | }); 30 | } 31 | 32 | var cleanPropertyName = function(name) { 33 | 34 | // turn things like 'align-items' into 'alignItems' 35 | name = name.replace(/(-.)/g, function(v) { return v[1].toUpperCase(); }) 36 | 37 | return name; 38 | } 39 | 40 | var mediaNameGenerator = function (name) { 41 | return '@media ' + name; 42 | }; 43 | 44 | var nameGenerator = function (name) { 45 | name = name.replace(/\s\s+/g, ' '); 46 | name = name.replace(/[^a-zA-Z0-9]/g, '_'); 47 | name = name.replace(/^_+/g, ''); 48 | name = name.replace(/_+$/g, ''); 49 | 50 | return name; 51 | }; 52 | 53 | export function transform (inputCssText) { 54 | 55 | if(!inputCssText) { 56 | throw new Error('missing css text to transform'); 57 | } 58 | 59 | // If the input "css" doesn't wrap it with a css class (raw styles) 60 | // we need to wrap it with a style so the css parser doesn't choke. 61 | var bootstrapWithCssClass = false; 62 | if(inputCssText.indexOf("{") === -1) { 63 | bootstrapWithCssClass = true; 64 | inputCssText = `.bootstrapWithCssClass { ${inputCssText} }`; 65 | } 66 | 67 | var css = cssParser.parse(inputCssText); 68 | var result = {}; 69 | transformRules(this, css.stylesheet.rules, result); 70 | 71 | // Don't expose the implementation detail of our wrapped css class. 72 | if(bootstrapWithCssClass) { 73 | result = result.bootstrapWithCssClass; 74 | } 75 | 76 | return result; 77 | } 78 | -------------------------------------------------------------------------------- /jspm_packages/github/jspm/nodelibs-process@0.2.0-alpha/process.js: -------------------------------------------------------------------------------- 1 | // From https://github.com/defunctzombie/node-process/blob/master/browser.js 2 | // shim for using process in browser 3 | 4 | var productionEnv = require('@system-env').production; 5 | 6 | var process = module.exports = {}; 7 | var queue = []; 8 | var draining = false; 9 | var currentQueue; 10 | var queueIndex = -1; 11 | 12 | function cleanUpNextTick() { 13 | draining = false; 14 | if (currentQueue.length) { 15 | queue = currentQueue.concat(queue); 16 | } else { 17 | queueIndex = -1; 18 | } 19 | if (queue.length) { 20 | drainQueue(); 21 | } 22 | } 23 | 24 | function drainQueue() { 25 | if (draining) { 26 | return; 27 | } 28 | var timeout = setTimeout(cleanUpNextTick); 29 | draining = true; 30 | 31 | var len = queue.length; 32 | while(len) { 33 | currentQueue = queue; 34 | queue = []; 35 | while (++queueIndex < len) { 36 | if (currentQueue) { 37 | currentQueue[queueIndex].run(); 38 | } 39 | } 40 | queueIndex = -1; 41 | len = queue.length; 42 | } 43 | currentQueue = null; 44 | draining = false; 45 | clearTimeout(timeout); 46 | } 47 | 48 | process.nextTick = function (fun) { 49 | var args = new Array(arguments.length - 1); 50 | if (arguments.length > 1) { 51 | for (var i = 1; i < arguments.length; i++) { 52 | args[i - 1] = arguments[i]; 53 | } 54 | } 55 | queue.push(new Item(fun, args)); 56 | if (queue.length === 1 && !draining) { 57 | setTimeout(drainQueue, 0); 58 | } 59 | }; 60 | 61 | // v8 likes predictible objects 62 | function Item(fun, array) { 63 | this.fun = fun; 64 | this.array = array; 65 | } 66 | Item.prototype.run = function () { 67 | this.fun.apply(null, this.array); 68 | }; 69 | process.title = 'browser'; 70 | process.browser = true; 71 | process.env = { 72 | NODE_ENV: productionEnv ? 'production' : 'development' 73 | }; 74 | process.argv = []; 75 | process.version = ''; // empty string to avoid regexp issues 76 | process.versions = {}; 77 | 78 | function noop() {} 79 | 80 | process.on = noop; 81 | process.addListener = noop; 82 | process.once = noop; 83 | process.off = noop; 84 | process.removeListener = noop; 85 | process.removeAllListeners = noop; 86 | process.emit = noop; 87 | 88 | process.binding = function (name) { 89 | throw new Error('process.binding is not supported'); 90 | }; 91 | 92 | process.cwd = function () { return '/' }; 93 | process.chdir = function (dir) { 94 | throw new Error('process.chdir is not supported'); 95 | }; 96 | process.umask = function() { return 0; }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "jspm install && npm run build && npm start", 4 | "build": "jspm bundle app - react - react-dom build.js -m", 5 | "start": "nws -o" 6 | }, 7 | "jspm": { 8 | "name": "app", 9 | "main": "app.js", 10 | "directories": { 11 | "lib": "src" 12 | }, 13 | "dependencies": { 14 | "css": "npm:css@^2.2.1", 15 | "react-dom": "npm:react-dom@^15.0.1" 16 | }, 17 | "devDependencies": { 18 | "babel-plugin-transform-react-jsx": "npm:babel-plugin-transform-react-jsx@^6.7.5", 19 | "net": "github:jspm/nodelibs-net@^0.2.0-alpha", 20 | "plugin-babel": "npm:systemjs-plugin-babel@^0.0.13", 21 | "tty": "github:jspm/nodelibs-tty@^0.2.0-alpha" 22 | }, 23 | "peerDependencies": { 24 | "assert": "github:jspm/nodelibs-assert@^0.2.0-alpha", 25 | "buffer": "github:jspm/nodelibs-buffer@^0.2.0-alpha", 26 | "child_process": "github:jspm/nodelibs-child_process@^0.2.0-alpha", 27 | "constants": "github:jspm/nodelibs-constants@^0.2.0-alpha", 28 | "core-js": "npm:core-js@^1.2.0", 29 | "crypto": "github:jspm/nodelibs-crypto@^0.2.0-alpha", 30 | "domain": "github:jspm/nodelibs-domain@^0.2.0-alpha", 31 | "events": "github:jspm/nodelibs-events@^0.2.0-alpha", 32 | "fs": "github:jspm/nodelibs-fs@^0.2.0-alpha", 33 | "http": "github:jspm/nodelibs-http@^0.2.0-alpha", 34 | "https": "github:jspm/nodelibs-https@^0.2.0-alpha", 35 | "module": "github:jspm/nodelibs-module@^0.2.0-alpha", 36 | "os": "github:jspm/nodelibs-os@^0.2.0-alpha", 37 | "path": "github:jspm/nodelibs-path@^0.2.0-alpha", 38 | "process": "github:jspm/nodelibs-process@^0.2.0-alpha", 39 | "react": "npm:react@^15.0.1", 40 | "stream": "github:jspm/nodelibs-stream@^0.2.0-alpha", 41 | "string_decoder": "github:jspm/nodelibs-string_decoder@^0.2.0-alpha", 42 | "url": "github:jspm/nodelibs-url@^0.2.0-alpha", 43 | "util": "github:jspm/nodelibs-util@^0.2.0-alpha", 44 | "vm": "github:jspm/nodelibs-vm@^0.2.0-alpha", 45 | "zlib": "github:jspm/nodelibs-zlib@^0.2.0-alpha" 46 | }, 47 | "overrides": { 48 | "npm:babel-runtime@5.8.38": { 49 | "main": false, 50 | "dependencies": {}, 51 | "optionalDependencies": { 52 | "core-js": "^1.2.0" 53 | } 54 | }, 55 | "npm:browserify-zlib@0.1.4": { 56 | "dependencies": { 57 | "readable-stream": "^2.0.2", 58 | "pako": "~0.2.0" 59 | }, 60 | "map": { 61 | "_stream_transform": "readable-stream/transform" 62 | } 63 | }, 64 | "npm:debug@2.2.0": { 65 | "main": "browser.js", 66 | "jspmNodeConversion": false, 67 | "format": "cjs", 68 | "map": { 69 | "./browser.js": { 70 | "node": "./node.js" 71 | }, 72 | "fs": "@node/fs", 73 | "net": "@node/net", 74 | "tty": "@node/tty", 75 | "util": "@node/util" 76 | } 77 | }, 78 | "npm:esprima@2.7.2": { 79 | "jspmNodeConversion": false 80 | }, 81 | "npm:inherits@2.0.1": { 82 | "ignore": [ 83 | "test.js" 84 | ] 85 | }, 86 | "npm:ms@0.7.1": { 87 | "jspmNodeConversion": false, 88 | "format": "cjs" 89 | }, 90 | "npm:readable-stream@1.0.34": { 91 | "map": { 92 | "stream": "stream-browserify/index" 93 | }, 94 | "systemjs": { 95 | "main": "readable.js" 96 | }, 97 | "dependencies": { 98 | "core-util-is": "~1.0.0", 99 | "isarray": "0.0.1", 100 | "string_decoder": "~0.10.0", 101 | "inherits": "~2.0.1", 102 | "stream-browserify": "~1.0.0" 103 | } 104 | }, 105 | "npm:whatwg-fetch@0.11.0": { 106 | "jspmNodeConversion": false 107 | } 108 | } 109 | }, 110 | "devDependencies": { 111 | "jspm": "^0.17.0-beta.28", 112 | "nws": "^1.1.1" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import {transform} from './transform.js'; 5 | 6 | class Input extends React.Component { 7 | componentDidUpdate(prevProps) { 8 | var node = ReactDOM.findDOMNode(this); 9 | var oldLength = node.value.length; 10 | var oldIdx = node.selectionStart; 11 | node.value = this.props.value; 12 | var newIdx = Math.max(0, node.value.length - oldLength + oldIdx); 13 | node.selectionStart = node.selectionEnd = newIdx; 14 | } 15 | 16 | render() { 17 | return