├── .gitignore
├── .npmignore
├── .babelrc
├── docs
├── entry.js
├── index.html
└── App.js
├── webpack.config.js
├── README.md
├── package.json
└── src
└── CSSEditor.js
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | docs
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "env",
4 | "stage-0",
5 | "react"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/docs/entry.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const { render } = require('react-dom')
3 | const App = require('./App')
4 |
5 | render(, app)
6 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 |
2 | const webpack = require('webpack')
3 | const path = require('path')
4 |
5 | module.exports = {
6 | entry: './docs/entry.js',
7 |
8 | output: {
9 | path: path.join(__dirname, 'docs'),
10 | filename: 'bundle.js'
11 | },
12 |
13 | node: {
14 | fs: 'empty'
15 | },
16 |
17 | resolve: {
18 | alias: {
19 | 'react-css-editor': path.join(__dirname, 'src/CSSEditor')
20 | }
21 | },
22 |
23 | module: {
24 | rules: [
25 | {
26 | test: /\.js$/,
27 | exclude: /node_modules/,
28 | use: 'babel-loader'
29 | }
30 | ]
31 | },
32 |
33 | plugins: [
34 | new webpack.DefinePlugin({
35 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
36 | })
37 | ],
38 |
39 | devServer: {
40 | contentBase: 'docs/',
41 | historyApiFallback: true
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # React CSS Editor
3 |
4 | React textarea component for editing style objects as CSS strings
5 |
6 | Demo: http://jxnblk.com/react-css-editor
7 |
8 | ```sh
9 | npm i react-css-editor
10 | ```
11 |
12 | ```jsx
13 | import React from 'react'
14 | import CSSEditor from 'react-css-editor'
15 |
16 | class App extends React.Component {
17 | state = {
18 | style: {
19 | color: 'tomato'
20 | padding: 16
21 | }
22 | }
23 |
24 | update = fn => {
25 | this.setState(fn)
26 | }
27 |
28 | render () {
29 | const { style } = this.state
30 |
31 | return (
32 |
33 | {
36 | this.update(state => ({ style: val }))
37 | })
38 | />
39 |
40 |
41 | )
42 | }
43 | }
44 | ```
45 |
46 | MIT License
47 |
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-css-editor",
3 | "version": "1.0.1",
4 | "description": "React textarea component for editing style objects as CSS strings",
5 | "main": "dist/CSSEditor.js",
6 | "scripts": {
7 | "dev": "webpack-dev-server",
8 | "docs": "NODE_ENV=production webpack",
9 | "prepublish": "babel src --out-dir dist",
10 | "test": "ava"
11 | },
12 | "keywords": [],
13 | "author": "Brent Jackson",
14 | "license": "MIT",
15 | "devDependencies": {
16 | "ava": "^0.19.1",
17 | "axs": "^1.0.6",
18 | "babel-cli": "^6.24.1",
19 | "babel-core": "^6.24.1",
20 | "babel-loader": "^7.0.0",
21 | "babel-preset-env": "^1.4.0",
22 | "babel-preset-react": "^6.24.1",
23 | "babel-preset-stage-0": "^6.24.1",
24 | "babel-register": "^6.24.1",
25 | "react": "^15.5.4",
26 | "react-dom": "^15.5.4",
27 | "react-test-renderer": "^15.5.4",
28 | "webpack": "^2.5.0",
29 | "webpack-dev-server": "^2.4.5"
30 | },
31 | "dependencies": {
32 | "css-to-object": "^1.0.0",
33 | "objss": "^1.0.2",
34 | "prop-types": "^15.5.8"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/CSSEditor.js:
--------------------------------------------------------------------------------
1 | const React = require('react')
2 | const PropTypes = require('prop-types')
3 | const objss = require('objss')
4 | const cssobj = require('css-to-object')
5 |
6 | class CSSEditor extends React.Component {
7 | state = {
8 | css: objss(this.props.value, {
9 | newline: true
10 | })
11 | }
12 |
13 | onChange = e => {
14 | const { value } = e.target
15 | this.setState({ css: value })
16 | try {
17 | const obj = cssobj(value)
18 | console.log('obj', obj)
19 | this.props.onChange(obj)
20 | } catch (e) {
21 | console.error(e)
22 | }
23 | }
24 |
25 | onKeyDown = e => {
26 | if (e.key === 'Tab') {
27 | e.preventDefault()
28 | const { css } = this.state
29 | const { selectionStart, selectionEnd } = this.root
30 | const next = css.slice(0, selectionStart) + ' ' + css.slice(selectionEnd)
31 | const position = selectionStart + 2
32 | this.setState({ css: next }, () => {
33 | this.root.selectionStart = position
34 | this.root.selectionEnd = position
35 | })
36 | }
37 | }
38 |
39 | render () {
40 | const {
41 | ...rest
42 | } = this.props
43 | const { css } = this.state
44 |
45 | return (
46 |