├── .bowerrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── example └── src │ ├── .gitignore │ ├── WidgetFactory.js │ ├── WidgetRenderer.js │ ├── example.css │ ├── example.js │ ├── example.less │ ├── excel.css │ ├── excel.less │ └── index.html ├── gulpfile.js ├── package.json └── src ├── PropertyEditor.js ├── editors ├── BgEditor.js ├── BindingEditor.js ├── BindingValueEditor.js ├── BorderEditor.js ├── BoxEditor.js ├── BoxSizeEditor.js ├── CodeEditor.js ├── ColorPickerEditor.js ├── FontEditor.js ├── GradientColorPickerEditor.js ├── GridEditor.js ├── HtmlEditor.js ├── JsonEditor.js ├── PageOptionsEditor.js ├── PlainJsonEditor.js ├── PlainTextEditor.js ├── PositionEditor.js └── WidgetStyleEditor.js └── utils ├── EmptyValue.js ├── ModalStyles.js ├── SelectValue.js ├── TooltipStyles.js ├── TruncateString.js ├── standardPageSizes.js └── toEmptyProps.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "example/dist/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is for unifying the coding style for different editors and IDEs 2 | # editorconfig.org 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = true 10 | indent_style = tab 11 | 12 | [*.json] 13 | indent_style = space 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .publish/* 2 | dist/* 3 | example/dist/* 4 | lib/* 5 | node_modules/* 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "plugins": [ 8 | "react" 9 | ], 10 | "rules": { 11 | "curly": [2, "multi-line"], 12 | "quotes": [2, "single", "avoid-escape"], 13 | "react/display-name": 0, 14 | "react/jsx-boolean-value": 1, 15 | "react/jsx-quotes": 1, 16 | "react/jsx-no-undef": 1, 17 | "react/jsx-sort-props": 0, 18 | "react/jsx-sort-prop-types": 1, 19 | "react/jsx-uses-react": 1, 20 | "react/jsx-uses-vars": 1, 21 | "react/no-did-mount-set-state": 1, 22 | "react/no-did-update-set-state": 1, 23 | "react/no-multi-comp": 1, 24 | "react/no-unknown-property": 1, 25 | "react/prop-types": 1, 26 | "react/react-in-jsx-scope": 1, 27 | "react/self-closing-comp": 1, 28 | "react/wrap-multilines": 1, 29 | "semi": 2, 30 | "strict": 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Coverage tools 11 | lib-cov 12 | coverage 13 | coverage.html 14 | .cover* 15 | 16 | # Dependency directory 17 | node_modules 18 | 19 | # Example build directory 20 | example/dist 21 | .publish 22 | 23 | # Editor and other tmp files 24 | *.swp 25 | *.un~ 26 | *.iml 27 | *.ipr 28 | *.iws 29 | *.sublime-* 30 | .idea/ 31 | *.DS_Store 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Roman Samec 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React property editor 2 | ======================= 3 | 4 | It is based on [react-json](https://github.com/arqex/react-json) and offers some predefined editors. 5 | 6 | + html editor - [react-tinymce](https://github.com/mzabriskie/react-tinymce) 7 | + code editor - [react-code-mirror](https://github.com/ForbesLindesay/react-code-mirror) 8 | + color picker - [react-color-picker](https://github.com/zippyui/react-color-picker) 9 | + binding editor - [react-binding](https://github.com/rsamec/react-binding) 10 | 11 | 12 | ## Demo & Examples 13 | 14 | [Live demo](http://rsamec.github.io/react-property-editor/) 15 | 16 | To build the examples locally, run: 17 | 18 | ``` 19 | npm install 20 | gulp dev 21 | ``` 22 | 23 | Then open [`localhost:8000`](http://localhost:8000) in a browser. 24 | 25 | 26 | ## Installation 27 | 28 | The easiest way to use this component is to install it from NPM and include it in your own React build process (using [Browserify](http://browserify.org), [Webpack](http://webpack.github.io/), etc). 29 | 30 | You can also use the standalone build by including `dist/react-pathjs-chart.js` in your page. If you use this, make sure you have already included React, and it is available as a global variable. 31 | 32 | ``` 33 | npm install react-pathjs-chart --save 34 | ``` 35 | 36 | 37 | ## Usage 38 | 39 | ``` 40 | import PropertyEditor from 'react-property-editor'; 41 | 42 | var App = React.createClass({ 43 | getInitialState(){ 44 | return { 45 | value: { 46 | name: "amigo", 47 | color: '', 48 | html: '', 49 | code: '', 50 | chart: { 51 | showLines: true, 52 | axisX: { 53 | showLines: true 54 | }, 55 | }, 56 | array: [1, 2, 3] 57 | } 58 | } 59 | }, 60 | logChange(value){ 61 | console.log(value); 62 | this.setState({value:value}); 63 | console.log(this.state.value); 64 | 65 | }, 66 | render() { 67 | return (
68 | 69 |
) 70 | } 71 | }); 72 | ``` 73 | 74 | ### Properties 75 | 76 | + value - value object - map the structure of your property grid to your object. 77 | + onChange(value) - when any property is changed the changed value object is passed as parameter 78 | 79 | ### Notes 80 | 81 | 82 | 83 | ### License 84 | 85 | MIT. Copyright (c) 2015 Roman Samec 86 | 87 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-property-editor", 3 | "version": "0.4.0", 4 | "description": "PropertyEditor", 5 | "main": "dist/react-property-editor.min.js", 6 | "homepage": "https://github.com/rsamec/react-property-editor", 7 | "authors": [ 8 | "Roman Samec" 9 | ], 10 | "moduleType": [ 11 | "amd", 12 | "globals", 13 | "node" 14 | ], 15 | "keywords": [ 16 | "react", 17 | "react-component" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | ".editorconfig", 22 | ".gitignore", 23 | "package.json", 24 | "src", 25 | "node_modules", 26 | "example", 27 | "test" 28 | ], 29 | "dependencies": { 30 | "tinymce": "~4.2.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/src/.gitignore: -------------------------------------------------------------------------------- 1 | ## This file is here to ensure it is included in the gh-pages branch, 2 | ## when `gulp deploy` is used to push updates to the demo site. 3 | 4 | # Dependency directory 5 | node_modules 6 | -------------------------------------------------------------------------------- /example/src/WidgetFactory.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import React from 'react'; 3 | var ReactBootstrap = require('react-bootstrap'); 4 | 5 | var bgStyle = function (source, panelSize) { 6 | var bg = source; 7 | var bgStyle = {}; 8 | if (source === undefined) return bgStyle; 9 | //size 10 | if (!!bg.size) { 11 | if (panelSize !== undefined && (bg.size === "leftHalf" || bg.size === "rightHalf")) { 12 | bgStyle.backgroundSize = `${panelSize.width * 2}px ${panelSize.height}px`; 13 | bgStyle.backgroundPosition = bg.size === "leftHalf" ? '0% 0%' : '100% 0%'; 14 | //console.log(bgStyle); 15 | } 16 | else { 17 | bgStyle.backgroundSize = bg.size; 18 | if (!!bg.position) bgStyle.backgroundPosition = bg.position; 19 | } 20 | } 21 | //gradient 22 | var bgGradient = bg.gradient; 23 | if (bgGradient !== undefined) { 24 | //gradient 25 | if (bgGradient.stops !== undefined) { 26 | var gradientStops = _.reduce(bgGradient.stops, function (memo, stop) { 27 | return memo + ', ' + stop.color + ' ' + (stop.offset * 100) + '%' 28 | }, ''); 29 | 30 | var orientation = bgGradient.orientation || 'top'; 31 | var grandientType = bgGradient.orientation === 'center, ellipse cover' ? '-webkit-radial-gradient' : '-webkit-linear-gradient'; 32 | bgStyle.background = grandientType + '(' + orientation + gradientStops + ')'; 33 | } 34 | } 35 | 36 | //color 37 | var bgColor = bg.color; 38 | if (bgColor !== undefined) { 39 | if (!!bgColor.color) bgStyle.backgroundColor = bgColor.color; 40 | if (!!bgColor.alpha) bgStyle.opacity = bgColor.alpha / 100; 41 | } 42 | 43 | if (!!bg.image) bgStyle.backgroundImage = 'url(' + bg.image + ')'; 44 | if (!!bg.repeat) bgStyle.backgroundRepeat = bg.repeat; 45 | if (!!bg.attachment) bgStyle.backgroundAttachment = bg.attachment; 46 | 47 | var filter = bg.filter || {}; 48 | var cssFilter = ""; 49 | if (!!filter.blur) cssFilter += ' blur(' + filter.blur + 'px)'; 50 | if (!!filter.brightness) cssFilter += ' brightness(' + filter.brightness + '%)'; 51 | if (!!filter.contrast) cssFilter += ' contrast(' + filter.contrast + '%)'; 52 | if (!!filter.grayscale) cssFilter += ' grayscale(' + filter.grayscale + '%)'; 53 | if (!!filter.hueRotate) cssFilter += ' hue-rotate(' + filter.hueRotate + 'deg)'; 54 | if (!!filter.invert) cssFilter += ' invert(' + filter.invert + '%)'; 55 | if (!!filter.opacity) cssFilter += ' opacity(' + filter.opacity + '%)'; 56 | if (!!filter.saturate) cssFilter += ' saturate(' + filter.saturate + '%)'; 57 | if (!!filter.sepia) cssFilter += ' sepia(' + filter.sepia + '%)'; 58 | 59 | if (!!cssFilter) { 60 | bgStyle.WebkitFilter = cssFilter; 61 | bgStyle.filter = cssFilter; 62 | } 63 | //bgStyle.position = 'absolute'; 64 | return bgStyle; 65 | } 66 | 67 | var Panel = (props) => { 68 | 69 | var style = _.extend(bgStyle(props.background), {width: 200, height: 200}); 70 | return
71 |
72 | }; 73 | 74 | var Widgets = {Panel: Panel}; 75 | 76 | var bootstrapWidgets = ['Input', 'Button', 'Panel', 'Glyphicon', 'Tooltip', 'Alert', 'Label', 'Well']; 77 | _.each(bootstrapWidgets, function (widgetName) { 78 | var name = 'react-bootstrap.' + widgetName; 79 | Widgets[name] = ReactBootstrap[widgetName]; 80 | }); 81 | 82 | 83 | var bootstrapSettings = { 84 | fields: { 85 | //content:{type:'string'}, 86 | bsSize: { 87 | type: 'select', settings: { 88 | options: _.map(['lg', 'large', 'sm', 'small'], function (key, value) { 89 | return {value: key, label: key}; 90 | }) 91 | } 92 | }, 93 | bsStyle: { 94 | type: 'select', settings: { 95 | options: _.map(['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'], function (key, value) { 96 | return {value: key, label: key}; 97 | }) 98 | } 99 | } 100 | } 101 | }; 102 | 103 | _.extend(Widgets['react-bootstrap.Button'], { 104 | metaData: { 105 | props: { 106 | bsSize: 'medium', bsStyle: 'default', content: 'Type content' 107 | }, 108 | settings: bootstrapSettings 109 | } 110 | }); 111 | _.extend(Widgets['react-bootstrap.Label'], { 112 | metaData: { 113 | props: { 114 | bsSize: 'medium', bsStyle: 'default', content: 'Type content' 115 | }, 116 | settings: bootstrapSettings 117 | } 118 | }); 119 | 120 | _.extend(Widgets['react-bootstrap.Panel'], { 121 | metaData: { 122 | props: { 123 | header: "Header", bsStyle: 'default', content: 'Type content' 124 | }, 125 | settings: bootstrapSettings 126 | } 127 | }); 128 | 129 | _.extend(Widgets['react-bootstrap.Glyphicon'], { 130 | metaData: { 131 | props: { 132 | bsSize: 'medium', bsStyle: 'default', glyph: 'star' 133 | }, 134 | settings: bootstrapSettings 135 | } 136 | }); 137 | 138 | _.extend(Widgets['react-bootstrap.Alert'], { 139 | metaData: { 140 | props: { 141 | bsSize: 'medium', bsStyle: 'default', content: 'Type content' 142 | }, 143 | settings: bootstrapSettings 144 | } 145 | }); 146 | 147 | _.extend(Widgets['react-bootstrap.Well'], { 148 | metaData: { 149 | props: { 150 | bsSize: 'lg', bsStyle: 'default', content: 'Type content', color: undefined 151 | }, 152 | settings: bootstrapSettings 153 | } 154 | }); 155 | 156 | _.extend(Widgets['react-bootstrap.Input'], { 157 | metaData: { 158 | props: { 159 | type: 'text', placeholder: 'type your text', label: 'label', help: undefined, value: '' 160 | }, 161 | settings: bootstrapSettings 162 | } 163 | }); 164 | const GridConfig = { 165 | // True if the first column in each row is a header (th) 166 | hasHeadColumn: true, 167 | // True if the data for the first column is just a string. 168 | // Set to false if you want to pass custom DOM elements. 169 | isHeadColumnString: true, 170 | // True if the first row is a header (th) 171 | hasHeadRow: true, 172 | // True if the data for the cells in the first row contains strings. 173 | // Set to false if you want to pass custom DOM elements. 174 | isHeadRowString: true, 175 | // True if the user can add rows (by navigating down from the last row) 176 | canAddRow: false, 177 | // True if the user can add columns (by navigating right from the last column) 178 | canAddColumn: false, 179 | // Override the display value for an empty cell 180 | emptyValueSymbol: '-', 181 | // Fills the first column with index numbers (1...n) and the first row with index letters (A...ZZZ) 182 | hasLetterNumberHeads: false 183 | }; 184 | 185 | _.extend(Panel, { 186 | metaData: { 187 | props: { 188 | color: undefined, 189 | gradient: undefined, 190 | background: undefined, 191 | styles: undefined, 192 | cGrid: undefined, 193 | pageOptions:undefined, 194 | grid: undefined, 195 | html:undefined, 196 | code:undefined 197 | }, 198 | settings: { 199 | fields: { 200 | styles: {type: 'widgetStyleEditor', settings: {widgets: Widgets}}, 201 | cGrid: { 202 | type: 'gridEditor', 203 | settings: { 204 | config: GridConfig, 205 | initialData: { 206 | rows: [ 207 | ['Key', 'AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF', 'GGG'], 208 | ['COM', '0,0', '0,1', '0,2', '0,3', '0,4', '0,5', '0,6'], 209 | ['DIV', '1,0', '1,1', '1,2', '1,3', '1,4', '1,5', '1,6'], 210 | ['DEV', '2,0', '2,1', '2,2', '2,3', '2,4', '2,5', '2,6'], 211 | ['ACC', '3,0', '3,1', '3,2', '3,3', '3,4', '3,5', '3,6'] 212 | ] 213 | }, 214 | converter: { 215 | parse: function (value) { 216 | return _.map(_.rest(value.rows,1), function (row, r) { 217 | var name = row[0]; 218 | return _.map(_.rest(row,1), function (cell, c) { 219 | var c = _.isString(cell) ? cell.replace(",", ".") : cell; 220 | var n = parseFloat(c); 221 | return {name: name, v: isNaN(n) ? c : n}; 222 | }) 223 | }) 224 | }, 225 | format: function (value) { 226 | var headRow = ['Key', 'AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'FFF', 'GGG']; 227 | var columns = ["COM","DIV","DEV","ACC"]; 228 | 229 | return { 230 | rows: [headRow].concat(_.map(value, function (row, r) { 231 | var name = columns[r]; 232 | return [name].concat(_.map(row, function (cell, c) { 233 | return cell.v; 234 | })) 235 | })) 236 | } 237 | } 238 | } 239 | } 240 | }, 241 | grid: {type: 'gridEditor'}, 242 | pageOptions:{type:'pageOptionsEditor'}, 243 | data: {type: 'jsonEditor'}, 244 | html: {type: 'htmlEditor'}, 245 | code: {type: 'codeEditor'} 246 | } 247 | } 248 | } 249 | }); 250 | 251 | export default Widgets; 252 | -------------------------------------------------------------------------------- /example/src/WidgetRenderer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Binder from 'react-binding'; 4 | 5 | var WidgetRenderer = React.createClass({ 6 | bindProps(node,dataBinder){ 7 | 8 | var props = _.cloneDeep(node.props); 9 | //go through all properties 10 | for (var propName in props) { 11 | var prop = props[propName]; 12 | 13 | var bindingProps = node.binding[propName]; 14 | 15 | //if binding -> replace binding props 16 | if (bindingProps !== undefined) { 17 | 18 | if (!!bindingProps.path) { 19 | //apply binding 20 | var converter; 21 | if (!!bindingProps.converter && !!bindingProps.converter.compiled) { 22 | converter = eval(bindingProps.converter.compiled); 23 | } 24 | var binding = Binder.bindTo(dataBinder, bindingProps.path, converter); 25 | 26 | if (bindingProps.mode === 'TwoWay') { 27 | //two-way binding 28 | props.valueLink = Binder.bindTo(dataBinder, bindingProps.path, converter); 29 | props[propName] = null; 30 | } 31 | else { 32 | //one-way binding 33 | //box[propName] = dataBinder.value[prop.Path]; 34 | props[propName] = binding.value; 35 | } 36 | } 37 | else { 38 | //binding is not correctly set - do not apply binding 39 | props[propName] = undefined; 40 | } 41 | } 42 | } 43 | return props; 44 | }, 45 | render(){ 46 | var box = this.props.node; 47 | var widget = this.props.widget; 48 | if (widget === undefined) { 49 | return React.DOM.span(null, 'Component ' + box.elementName + ' is not register among widgets.'); 50 | } 51 | 52 | //apply binding 53 | var props =(this.props.dataBinder !== undefined) ? this.bindProps(box,this.props.dataBinder):box.props; 54 | 55 | 56 | //apply property resolution strategy -> default style -> custom style -> local style 57 | var customStyle= this.props.customStyle; 58 | var widgetStyle = _.cloneDeep(widget.metaData.props || {}); 59 | if (customStyle !== undefined) widgetStyle = _.merge(widgetStyle,customStyle); 60 | props = _.merge(widgetStyle,props); 61 | 62 | //TODO: consider using 63 | 64 | 65 | return React.createElement(widget,props,props.content !== undefined ? React.DOM.div({ dangerouslySetInnerHTML: {__html: props.content } }) : null); 66 | } 67 | }); 68 | export default WidgetRenderer; 69 | //WidgetRenderer.propTypes = { widget: React.PropTypes.node, value:React.PropTypes.object,dataBinder:React.PropTypes.object }; 70 | -------------------------------------------------------------------------------- /example/src/example.css: -------------------------------------------------------------------------------- 1 | /* 2 | // Examples Stylesheet 3 | // ------------------- 4 | */ 5 | @import "../../node_modules/react-colors-picker/assets/index.css"; 6 | body { 7 | font-family: Helvetica Neue, Helvetica, Arial, sans-serif; 8 | font-size: 14px; 9 | color: #333; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | a { 14 | color: #08c; 15 | text-decoration: none; 16 | } 17 | a:hover { 18 | text-decoration: underline; 19 | } 20 | .container { 21 | margin-left: auto; 22 | margin-right: auto; 23 | max-width: 720px; 24 | padding: 1em; 25 | } 26 | .footer { 27 | margin-top: 50px; 28 | border-top: 1px solid #eee; 29 | padding: 20px 0; 30 | font-size: 12px; 31 | color: #999; 32 | } 33 | h1, 34 | h2, 35 | h3, 36 | h4, 37 | h5, 38 | h6 { 39 | color: #222; 40 | font-weight: 100; 41 | margin: 0.5em 0; 42 | } 43 | label { 44 | color: #999; 45 | display: inline-block; 46 | font-size: 0.85em; 47 | font-weight: bold; 48 | margin: 1em 0; 49 | text-transform: uppercase; 50 | } 51 | .hint { 52 | margin: 15px 0; 53 | font-style: italic; 54 | color: #999; 55 | } 56 | .react-colorpicker-panel-inner { 57 | position: relative; 58 | border-radius: 4px; 59 | box-shadow: 0 1px 5px #ccc; 60 | border: 1px solid #ccc; 61 | padding: 8px; 62 | } 63 | .react-colorpicker-panel-wrap { 64 | margin: 5px 0 0; 65 | height: 30px; 66 | width: 100%; 67 | position: relative; 68 | } 69 | .react-colorpicker-panel-wrap-preview { 70 | position: absolute; 71 | right: 0px; 72 | } 73 | .react-colorpicker-panel-wrap-ribbon { 74 | position: absolute; 75 | left: 0px; 76 | top: 0; 77 | right: 35px; 78 | height: 12.5px; 79 | } 80 | .react-colorpicker-panel-wrap-alpha { 81 | position: absolute; 82 | left: 0px; 83 | right: 35px; 84 | bottom: 0; 85 | height: 12.5px; 86 | } 87 | .react-colorpicker-trigger { 88 | border: 1px solid #999; 89 | display: inline-block; 90 | padding: 2px; 91 | border-radius: 2px; 92 | -webkit-user-select: none; 93 | -moz-user-select: none; 94 | -ms-user-select: none; 95 | user-select: none; 96 | width: 20px; 97 | height: 20px; 98 | cursor: pointer; 99 | box-shadow: 0 0 0 2px #fff inset; 100 | } 101 | .react-colorpicker-trigger-open { 102 | box-shadow: 0px 0px 3px #999; 103 | } 104 | .react-colorpicker-panel { 105 | width: 218px; 106 | background-color: #fff; 107 | box-sizing: border-box; 108 | outline: none; 109 | z-index: 9; 110 | -moz-user-select: none; 111 | -khtml-user-select: none; 112 | -webkit-user-select: none; 113 | -ms-user-select: none; 114 | user-select: none; 115 | } 116 | .react-colorpicker-panel * { 117 | box-sizing: border-box; 118 | } 119 | .react-colorpicker-panel-open { 120 | display: block; 121 | } 122 | .react-colorpicker-panel-close { 123 | display: none; 124 | } 125 | .react-colorpicker-panel-preview { 126 | height: 30px; 127 | width: 30px; 128 | overflow: hidden; 129 | border-radius: 2px; 130 | box-shadow: 0 0 2px #808080 inset; 131 | background-image: url('data:image/png;base64,R0lGODdhCgAKAPAAAOXl5f///ywAAAAACgAKAEACEIQdqXt9GxyETrI279OIgwIAOw=='); 132 | } 133 | .react-colorpicker-panel-preview span, 134 | .react-colorpicker-panel-preview input[type=color] { 135 | position: absolute; 136 | display: block; 137 | height: 100%; 138 | width: 30px; 139 | border-radius: 2px; 140 | } 141 | .react-colorpicker-panel-preview input[type=color] { 142 | opacity: 0; 143 | } 144 | .react-colorpicker-panel-board { 145 | position: relative; 146 | font-size: 0; 147 | -webkit-user-select: none; 148 | -moz-user-select: none; 149 | -ms-user-select: none; 150 | user-select: none; 151 | } 152 | .react-colorpicker-panel-board span { 153 | position: absolute; 154 | border-radius: 10px; 155 | border: 1px solid #fff; 156 | width: 9px; 157 | height: 9px; 158 | left: -999px; 159 | top: -999px; 160 | box-shadow: 0 0 1px rgba(120, 120, 120, 0.7); 161 | z-index: 2; 162 | } 163 | .react-colorpicker-panel-board-hsv { 164 | width: 200px; 165 | height: 150px; 166 | position: relative; 167 | z-index: 1; 168 | border-radius: 2px; 169 | } 170 | .react-colorpicker-panel-board-value { 171 | border-radius: 2px; 172 | position: absolute; 173 | width: 100%; 174 | height: 100%; 175 | left: 0; 176 | top: 0; 177 | z-index: 2; 178 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 179 | background-image: linear-gradient(to bottom, transparent 0%, #000000 100%); 180 | } 181 | .react-colorpicker-panel-board-saturation { 182 | border-radius: 2px; 183 | position: absolute; 184 | width: 100%; 185 | height: 100%; 186 | left: 0; 187 | top: 0; 188 | z-index: 1; 189 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0icmdiKDAsMCwwKSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 190 | background-image: linear-gradient(to right, #ffffff 0%, transparent 100%); 191 | } 192 | .react-colorpicker-panel-board-handler { 193 | box-shadow: 0 0 2px #808080 inset; 194 | border-radius: 2px; 195 | cursor: crosshair; 196 | -webkit-user-select: none; 197 | -moz-user-select: none; 198 | -ms-user-select: none; 199 | user-select: none; 200 | position: absolute; 201 | top: 0; 202 | left: 0; 203 | width: 100%; 204 | height: 100%; 205 | z-index: 3; 206 | } 207 | .react-colorpicker-panel-ribbon { 208 | position: relative; 209 | height: 100%; 210 | border-radius: 2px; 211 | box-shadow: 0 0 2px #808080 inset; 212 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZjAwMDAiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmY5OTAwIiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjIwJSIgc3RvcC1jb2xvcj0iI2NkZmYwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSIzMCUiIHN0b3AtY29sb3I9IiMzNWZmMDAiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjMDBmZjY2IiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwZmZmZCIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiMwMDY2ZmYiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iNzAlIiBzdG9wLWNvbG9yPSIjMzIwMGZmIiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iI2NkMDBmZiIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZjAwOTkiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 213 | background-image: linear-gradient(to right, #ff0000 0%, #ff9900 10%, #cdff00 20%, #35ff00 30%, #00ff66 40%, #00fffd 50%, #0066ff 60%, #3200ff 70%, #cd00ff 80%, #ff0099 90%, #ff0000 100%); 214 | } 215 | .react-colorpicker-panel-ribbon span { 216 | position: absolute; 217 | top: 0; 218 | height: 100%; 219 | width: 4px; 220 | border: 1px solid #000000; 221 | padding: 1px 0; 222 | margin-left: -2px; 223 | background-color: #fff; 224 | border-radius: 3px; 225 | } 226 | .react-colorpicker-panel-ribbon-handler { 227 | position: absolute; 228 | width: 104%; 229 | height: 100%; 230 | left: -2%; 231 | cursor: pointer; 232 | } 233 | .react-colorpicker-panel-alpha { 234 | position: relative; 235 | height: 100%; 236 | width: 100%; 237 | border-radius: 2px; 238 | background-image: url('data:image/png;base64,R0lGODdhCgAKAPAAAOXl5f///ywAAAAACgAKAEACEIQdqXt9GxyETrI279OIgwIAOw=='); 239 | background-repeat: repeat; 240 | -webkit-user-select: none; 241 | -moz-user-select: none; 242 | -ms-user-select: none; 243 | user-select: none; 244 | } 245 | .react-colorpicker-panel-alpha-bg { 246 | position: absolute; 247 | width: 100%; 248 | height: 100%; 249 | border-radius: 2px; 250 | box-shadow: 0 0 2px #808080 inset; 251 | } 252 | .react-colorpicker-panel-alpha span { 253 | position: absolute; 254 | top: 0; 255 | height: 100%; 256 | width: 4px; 257 | border: 1px solid #000000; 258 | padding: 1px 0; 259 | margin-left: -2px; 260 | background-color: #fff; 261 | border-radius: 3px; 262 | } 263 | .react-colorpicker-panel-alpha-handler { 264 | position: absolute; 265 | width: 104%; 266 | height: 100%; 267 | left: -2%; 268 | cursor: pointer; 269 | } 270 | .react-colorpicker-panel-params { 271 | font-size: 12px; 272 | } 273 | .react-colorpicker-panel-params-input { 274 | overflow: hidden; 275 | padding: 2px 0; 276 | } 277 | .react-colorpicker-panel-params input { 278 | -webkit-user-select: text; 279 | -moz-user-select: text; 280 | -ms-user-select: text; 281 | user-select: text; 282 | text-align: center; 283 | padding: 1px; 284 | margin: 0; 285 | float: left; 286 | border-radius: 2px; 287 | border: 1px solid #CACACA; 288 | font-family: 'Helvetica Neue', Helvetica, sans-serif; 289 | } 290 | .react-colorpicker-panel-params-hex { 291 | width: 52px; 292 | } 293 | .react-colorpicker-panel-params input[type=number] { 294 | margin-left: 5px; 295 | width: 32px; 296 | } 297 | .react-colorpicker-panel-params input[type=number]::-webkit-inner-spin-button { 298 | -webkit-appearance: none; 299 | } 300 | .react-colorpicker-panel-params-lable { 301 | padding: 2px 0; 302 | height: 22px; 303 | line-height: 18px; 304 | -webkit-user-select: none; 305 | -moz-user-select: none; 306 | -ms-user-select: none; 307 | user-select: none; 308 | } 309 | .react-colorpicker-panel-params-lable label { 310 | float: left; 311 | text-align: center; 312 | } 313 | .react-colorpicker-panel-params-lable-hex { 314 | width: 52px; 315 | } 316 | .react-colorpicker-panel-params-lable-number, 317 | .react-colorpicker-panel-params-lable-alpha { 318 | margin-left: 5px; 319 | width: 32px; 320 | } 321 | .react-colorpicker-panel-params-lable-number:hover { 322 | border-radius: 2px; 323 | background-color: #eee; 324 | box-shadow: 0 0 0 1px #ccc inset; 325 | cursor: pointer; 326 | } 327 | .react-colorpicker { 328 | font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; 329 | } 330 | .react-colorpicker-picker { 331 | position: absolute; 332 | left: -9999px; 333 | top: -9999px; 334 | z-index: 1000; 335 | } 336 | .react-colorpicker-picker-slide-up-enter { 337 | -webkit-animation-duration: .3s; 338 | animation-duration: .3s; 339 | -webkit-animation-fill-mode: both; 340 | animation-fill-mode: both; 341 | -webkit-transform-origin: 0 0; 342 | -ms-transform-origin: 0 0; 343 | transform-origin: 0 0; 344 | display: block !important; 345 | opacity: 0; 346 | -webkit-animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 347 | animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 348 | -webkit-animation-play-state: paused; 349 | animation-play-state: paused; 350 | } 351 | .react-colorpicker-picker-slide-up-appear { 352 | -webkit-animation-duration: .3s; 353 | animation-duration: .3s; 354 | -webkit-animation-fill-mode: both; 355 | animation-fill-mode: both; 356 | -webkit-transform-origin: 0 0; 357 | -ms-transform-origin: 0 0; 358 | transform-origin: 0 0; 359 | display: block !important; 360 | opacity: 0; 361 | -webkit-animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 362 | animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 363 | -webkit-animation-play-state: paused; 364 | animation-play-state: paused; 365 | } 366 | .react-colorpicker-picker-slide-up-leave { 367 | -webkit-animation-duration: .3s; 368 | animation-duration: .3s; 369 | -webkit-animation-fill-mode: both; 370 | animation-fill-mode: both; 371 | -webkit-transform-origin: 0 0; 372 | -ms-transform-origin: 0 0; 373 | transform-origin: 0 0; 374 | display: block !important; 375 | opacity: 1; 376 | -webkit-animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); 377 | animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); 378 | -webkit-animation-play-state: paused; 379 | animation-play-state: paused; 380 | } 381 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-bottomLeft, 382 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-bottomRight, 383 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-bottomLeft, 384 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-bottomRight { 385 | -webkit-animation-name: rcDropdownSlideUpIn; 386 | animation-name: rcDropdownSlideUpIn; 387 | -webkit-animation-play-state: running; 388 | animation-play-state: running; 389 | } 390 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-topLeft, 391 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-topRight, 392 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-topLeft, 393 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-topRight { 394 | -webkit-animation-name: rcDropdownSlideDownIn; 395 | animation-name: rcDropdownSlideDownIn; 396 | -webkit-animation-play-state: running; 397 | animation-play-state: running; 398 | } 399 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-bottomLeft, 400 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-bottomRight { 401 | -webkit-animation-name: rcDropdownSlideUpOut; 402 | animation-name: rcDropdownSlideUpOut; 403 | -webkit-animation-play-state: running; 404 | animation-play-state: running; 405 | } 406 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-topLeft, 407 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-topRight { 408 | -webkit-animation-name: rcDropdownSlideDownOut; 409 | animation-name: rcDropdownSlideDownOut; 410 | -webkit-animation-play-state: running; 411 | animation-play-state: running; 412 | } 413 | @-webkit-keyframes rcDropdownSlideUpIn { 414 | 0% { 415 | opacity: 0; 416 | -webkit-transform-origin: 0% 0%; 417 | transform-origin: 0% 0%; 418 | -webkit-transform: scaleY(0); 419 | transform: scaleY(0); 420 | } 421 | 100% { 422 | opacity: 1; 423 | -webkit-transform-origin: 0% 0%; 424 | transform-origin: 0% 0%; 425 | -webkit-transform: scaleY(1); 426 | transform: scaleY(1); 427 | } 428 | } 429 | @keyframes rcDropdownSlideUpIn { 430 | 0% { 431 | opacity: 0; 432 | -webkit-transform-origin: 0% 0%; 433 | transform-origin: 0% 0%; 434 | -webkit-transform: scaleY(0); 435 | transform: scaleY(0); 436 | } 437 | 100% { 438 | opacity: 1; 439 | -webkit-transform-origin: 0% 0%; 440 | transform-origin: 0% 0%; 441 | -webkit-transform: scaleY(1); 442 | transform: scaleY(1); 443 | } 444 | } 445 | @-webkit-keyframes rcDropdownSlideUpOut { 446 | 0% { 447 | opacity: 1; 448 | -webkit-transform-origin: 0% 0%; 449 | transform-origin: 0% 0%; 450 | -webkit-transform: scaleY(1); 451 | transform: scaleY(1); 452 | } 453 | 100% { 454 | opacity: 0; 455 | -webkit-transform-origin: 0% 0%; 456 | transform-origin: 0% 0%; 457 | -webkit-transform: scaleY(0); 458 | transform: scaleY(0); 459 | } 460 | } 461 | @keyframes rcDropdownSlideUpOut { 462 | 0% { 463 | opacity: 1; 464 | -webkit-transform-origin: 0% 0%; 465 | transform-origin: 0% 0%; 466 | -webkit-transform: scaleY(1); 467 | transform: scaleY(1); 468 | } 469 | 100% { 470 | opacity: 0; 471 | -webkit-transform-origin: 0% 0%; 472 | transform-origin: 0% 0%; 473 | -webkit-transform: scaleY(0); 474 | transform: scaleY(0); 475 | } 476 | } 477 | @-webkit-keyframes rcDropdownSlideDownIn { 478 | 0% { 479 | opacity: 0; 480 | -webkit-transform-origin: 100% 100%; 481 | transform-origin: 100% 100%; 482 | -webkit-transform: scaleY(0); 483 | transform: scaleY(0); 484 | } 485 | 100% { 486 | opacity: 1; 487 | -webkit-transform-origin: 100% 100%; 488 | transform-origin: 100% 100%; 489 | -webkit-transform: scaleY(1); 490 | transform: scaleY(1); 491 | } 492 | } 493 | @keyframes rcDropdownSlideDownIn { 494 | 0% { 495 | opacity: 0; 496 | -webkit-transform-origin: 100% 100%; 497 | transform-origin: 100% 100%; 498 | -webkit-transform: scaleY(0); 499 | transform: scaleY(0); 500 | } 501 | 100% { 502 | opacity: 1; 503 | -webkit-transform-origin: 100% 100%; 504 | transform-origin: 100% 100%; 505 | -webkit-transform: scaleY(1); 506 | transform: scaleY(1); 507 | } 508 | } 509 | @-webkit-keyframes rcDropdownSlideDownOut { 510 | 0% { 511 | opacity: 1; 512 | -webkit-transform-origin: 100% 100%; 513 | transform-origin: 100% 100%; 514 | -webkit-transform: scaleY(1); 515 | transform: scaleY(1); 516 | } 517 | 100% { 518 | opacity: 0; 519 | -webkit-transform-origin: 100% 100%; 520 | transform-origin: 100% 100%; 521 | -webkit-transform: scaleY(0); 522 | transform: scaleY(0); 523 | } 524 | } 525 | @keyframes rcDropdownSlideDownOut { 526 | 0% { 527 | opacity: 1; 528 | -webkit-transform-origin: 100% 100%; 529 | transform-origin: 100% 100%; 530 | -webkit-transform: scaleY(1); 531 | transform: scaleY(1); 532 | } 533 | 100% { 534 | opacity: 0; 535 | -webkit-transform-origin: 100% 100%; 536 | transform-origin: 100% 100%; 537 | -webkit-transform: scaleY(0); 538 | transform: scaleY(0); 539 | } 540 | } 541 | .gc-container { 542 | width: 100%; 543 | margin: 0 0; 544 | padding: 0 0; 545 | position: relative; 546 | } 547 | .gc-container .react-colorpicker-trigger { 548 | width: 20px; 549 | height: 20px; 550 | } 551 | .gc-container .gc-canvas { 552 | width: 100%; 553 | height: 20px; 554 | background-color: white; 555 | } 556 | .gc-container svg { 557 | position: absolute; 558 | top: 0px; 559 | left: 0px; 560 | } 561 | .gc-container canvas { 562 | position: absolute; 563 | top: 0px; 564 | left: 0px; 565 | } 566 | .gc-container .gc-handler { 567 | fill: white; 568 | stroke: gray; 569 | fill-opacity: 0.7; 570 | cursor: move; 571 | } 572 | .gc-container .gc-colorpicker { 573 | position: absolute; 574 | } 575 | .gc-colorpicker .remove-btn { 576 | position: relative; 577 | position: absolute; 578 | top: 15px; 579 | left: 4px; 580 | cursor: pointer; 581 | opacity: 0; 582 | } 583 | .gc-colorpicker:hover .remove-btn { 584 | opacity: 1; 585 | } 586 | .react-colorpicker { 587 | border: 1px solid #999; 588 | padding: 0px; 589 | border-radius: 2px; 590 | } 591 | .react-colorpicker-trigger { 592 | border: 0px; 593 | margin-bottom: -2px; 594 | } 595 | /* JSON Editor */ 596 | .jsonEditor * { 597 | vertical-align: top; 598 | } 599 | .jsonEditor > .jsonObject > .jsonChildren { 600 | margin-left: 0; 601 | border-left: 0; 602 | padding: 0; 603 | } 604 | .jsonChildren { 605 | margin-left: 30px; 606 | display: none; 607 | font-weight: normal; 608 | border-left: 1px dotted #ddd; 609 | } 610 | .jsonFlex .jsonChildren { 611 | margin-left: -30px; 612 | } 613 | .jsonCompound.open > .jsonChildren { 614 | display: block; 615 | } 616 | .jsonAdd { 617 | margin-left: 5px; 618 | } 619 | .jsonFlex .jsonField { 620 | display: -ms-flexbox; 621 | display: -webkit-flex; 622 | display: flex; 623 | -webkit-flex-direction: row; 624 | -ms-flex-direction: row; 625 | flex-direction: row; 626 | -webkit-flex-wrap: nowrap; 627 | -ms-flex-wrap: nowrap; 628 | flex-wrap: nowrap; 629 | -webkit-justify-content: flex-start; 630 | -ms-flex-pack: start; 631 | justify-content: flex-start; 632 | -webkit-align-content: stretch; 633 | -ms-flex-line-pack: stretch; 634 | align-content: stretch; 635 | } 636 | .jsonFieldAdder { 637 | margin-left: 13px; 638 | } 639 | .jsonFieldAdder select { 640 | margin-left: 5px; 641 | } 642 | .compoundToggle:before { 643 | content: '▸'; 644 | color: #333; 645 | line-height: 1em; 646 | margin-right: 3px; 647 | } 648 | .open > .compoundToggle:before { 649 | content: '▾'; 650 | } 651 | .jsonName { 652 | font-style: italic; 653 | position: relative; 654 | padding-left: 15px; 655 | white-space: nowrap; 656 | min-width: 80px; 657 | } 658 | .jsonValue { 659 | position: relative; 660 | padding-left: 15px; 661 | flex-grow: 1; 662 | } 663 | .jsonValue > input, 664 | .jsonValue > textarea, 665 | .jsonValue > select { 666 | margin-bottom: 5px; 667 | } 668 | .jsonFlex input[type=text], 669 | .jsonFlex input[type=number], 670 | .jsonFlex input[type=password], 671 | .jsonFlex textarea { 672 | width: 100%; 673 | } 674 | .jsonRemove, 675 | .jsonFixed { 676 | visibility: hidden; 677 | margin-right: 5px; 678 | text-decoration: none; 679 | width: .6em; 680 | display: inline-block; 681 | position: absolute; 682 | left: 0; 683 | } 684 | .jsonName:hover > .jsonRemove { 685 | visibility: visible; 686 | } 687 | .jsonReset { 688 | visibility: hidden; 689 | padding-left: 0px; 690 | text-decoration: none; 691 | width: .6em; 692 | display: inline-block; 693 | position: absolute; 694 | left: 0; 695 | } 696 | .jsonValue:hover > .jsonReset { 697 | visibility: visible; 698 | } 699 | .jsonBind { 700 | visibility: hidden; 701 | padding-left: 10px; 702 | text-decoration: none; 703 | width: .6em; 704 | display: inline-block; 705 | position: absolute; 706 | left: 0; 707 | } 708 | .jsonValue:hover > .jsonBind { 709 | visibility: visible; 710 | } 711 | .compoundToggle, 712 | .jsonString { 713 | cursor: pointer; 714 | } 715 | .booleanField * { 716 | vertical-align: middle; 717 | } 718 | .booleanField input { 719 | margin-bottom: 0; 720 | } 721 | .jsonError > .jsonName, 722 | .jsonError .jsonErrorMsg { 723 | color: #f36; 724 | } 725 | .jsonError .jsonErrorMsg { 726 | margin: 0 0 10px 10px; 727 | font-size: 0.9em; 728 | vertical-align: bottom; 729 | display: block; 730 | } 731 | .jsonError > .jsonValue > input, 732 | .jsonError > .jsonValue > select, 733 | .jsonError > .jsonValue > textarea { 734 | background-color: #fff8f8; 735 | } 736 | .jsonNovalue { 737 | color: #999; 738 | font-style: italic; 739 | } 740 | .jsonGroup { 741 | margin-bottom: 10px; 742 | } 743 | .bindingEditor .jsonEditor { 744 | display: inline-block; 745 | } 746 | .flex-container { 747 | display: flex; 748 | flex-direction: row; 749 | flex-wrap: wrap; 750 | justify-content: space-between; 751 | } 752 | .flex-item { 753 | margin: 10px; 754 | text-align: center; 755 | min-width: 150px; 756 | } 757 | .flex-item .thumb { 758 | background-color: gainsboro; 759 | border-radius: 4px; 760 | display: inline-block; 761 | } 762 | .flex-item .thumb:hover { 763 | border: 1px solid cadetblue; 764 | } 765 | .flex-item span { 766 | vertical-align: middle; 767 | text-align: center; 768 | } 769 | .flex-item .footer { 770 | text-align: center; 771 | min-width: 100%; 772 | } 773 | -------------------------------------------------------------------------------- /example/src/example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import _ from 'lodash'; 4 | import PropertyEditor from 'react-property-editor'; 5 | import Binder from 'react-binding'; 6 | import Json from 'react-json-fork'; 7 | import WidgetFactory from './WidgetFactory.js'; 8 | import WidgetRenderer from './WidgetRenderer.js'; 9 | 10 | var options = _.mapValues(WidgetFactory,function(value,key,object){return {value:key,name:key} }); 11 | var widgets = WidgetFactory; 12 | var defaultTheme = {}; 13 | var defaultSelectedItem = 'Panel'; 14 | 15 | class App extends React.Component 16 | { 17 | constructor(props){ 18 | super(props); 19 | var widget = widgets[defaultSelectedItem]; 20 | this.state = { 21 | data: { text:'hello world'}, 22 | selected: defaultSelectedItem, 23 | current: { 24 | widget: widget, 25 | props: _.cloneDeep(widget.metaData && widget.metaData.props), 26 | binding:{value:{path:''}} 27 | } 28 | } 29 | } 30 | logChange(value){ 31 | this.setState({ 32 | current: { 33 | widget: this.state.current.widget, 34 | props: value.props, 35 | binding:value.binding 36 | } 37 | } 38 | ); 39 | } 40 | selectChange(e){ 41 | var widget = widgets[e.target.value]; 42 | this.setState({ 43 | selected: e.target.value, 44 | current: { 45 | widget: widget, 46 | props: _.cloneDeep(widget.metaData && widget.metaData.props), 47 | binding:{} 48 | } 49 | }) 50 | } 51 | render() { 52 | var dataBinder = Binder.bindToState(this,'data'); 53 | var widget = this.state.current.widget; 54 | var customSettings = widget.metaData && widget.metaData.settings || {}; 55 | var customStyle = defaultTheme[this.state.selected]; 56 | return ( 57 |
58 |
59 |
60 | 67 |
68 |
69 |
70 |
71 | 73 |
74 |
75 | 77 |
78 |
{JSON.stringify(this.state.current.props, null, 2)}
79 |
{JSON.stringify(this.state.current.binding, null, 2)}
80 |
81 |
82 |
83 | ) 84 | } 85 | }; 86 | ReactDOM.render(, document.getElementById('app')); 87 | -------------------------------------------------------------------------------- /example/src/example.less: -------------------------------------------------------------------------------- 1 | /* 2 | // Examples Stylesheet 3 | // ------------------- 4 | */ 5 | 6 | body { 7 | font-family: Helvetica Neue, Helvetica, Arial, sans-serif; 8 | font-size: 14px; 9 | color: #333; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | a { 15 | color: #08c; 16 | text-decoration: none; 17 | } 18 | 19 | a:hover { 20 | text-decoration: underline; 21 | } 22 | 23 | .container { 24 | margin-left: auto; 25 | margin-right: auto; 26 | max-width: 720px; 27 | padding: 1em; 28 | } 29 | 30 | .footer { 31 | margin-top: 50px; 32 | border-top: 1px solid #eee; 33 | padding: 20px 0; 34 | font-size: 12px; 35 | color: #999; 36 | } 37 | 38 | h1, h2, h3, h4, h5, h6 { 39 | color: #222; 40 | font-weight: 100; 41 | margin: 0.5em 0; 42 | } 43 | 44 | label { 45 | color: #999; 46 | display: inline-block; 47 | font-size: 0.85em; 48 | font-weight: bold; 49 | margin: 1em 0; 50 | text-transform: uppercase; 51 | } 52 | 53 | .hint { 54 | margin: 15px 0; 55 | font-style: italic; 56 | color: #999; 57 | } 58 | 59 | @import "../../node_modules/react-colors-picker/assets/index.css"; 60 | .react-colorpicker-panel-inner { 61 | position: relative; 62 | border-radius: 4px; 63 | box-shadow: 0 1px 5px #ccc; 64 | border: 1px solid #ccc; 65 | padding: 8px; 66 | } 67 | .react-colorpicker-panel-wrap { 68 | margin: 5px 0 0; 69 | height: 30px; 70 | width: 100%; 71 | position: relative; 72 | } 73 | .react-colorpicker-panel-wrap-preview { 74 | position: absolute; 75 | right: 0px; 76 | } 77 | .react-colorpicker-panel-wrap-ribbon { 78 | position: absolute; 79 | left: 0px; 80 | top: 0; 81 | right: 35px; 82 | height: 12.5px; 83 | } 84 | .react-colorpicker-panel-wrap-alpha { 85 | position: absolute; 86 | left: 0px; 87 | right: 35px; 88 | bottom: 0; 89 | height: 12.5px; 90 | } 91 | .react-colorpicker-trigger { 92 | border: 1px solid #999; 93 | display: inline-block; 94 | padding: 2px; 95 | border-radius: 2px; 96 | -webkit-user-select: none; 97 | -moz-user-select: none; 98 | -ms-user-select: none; 99 | user-select: none; 100 | width: 20px; 101 | height: 20px; 102 | cursor: pointer; 103 | box-shadow: 0 0 0 2px #fff inset; 104 | } 105 | .react-colorpicker-trigger-open { 106 | box-shadow: 0px 0px 3px #999; 107 | } 108 | .react-colorpicker-panel { 109 | width: 218px; 110 | background-color: #fff; 111 | box-sizing: border-box; 112 | outline: none; 113 | z-index: 9; 114 | -moz-user-select: none; 115 | -khtml-user-select: none; 116 | -webkit-user-select: none; 117 | -ms-user-select: none; 118 | user-select: none; 119 | } 120 | .react-colorpicker-panel * { 121 | box-sizing: border-box; 122 | } 123 | .react-colorpicker-panel-open { 124 | display: block; 125 | } 126 | .react-colorpicker-panel-close { 127 | display: none; 128 | } 129 | .react-colorpicker-panel-preview { 130 | height: 30px; 131 | width: 30px; 132 | overflow: hidden; 133 | border-radius: 2px; 134 | box-shadow: 0 0 2px #808080 inset; 135 | background-image: url('data:image/png;base64,R0lGODdhCgAKAPAAAOXl5f///ywAAAAACgAKAEACEIQdqXt9GxyETrI279OIgwIAOw=='); 136 | } 137 | .react-colorpicker-panel-preview span, 138 | .react-colorpicker-panel-preview input[type=color] { 139 | position: absolute; 140 | display: block; 141 | height: 100%; 142 | width: 30px; 143 | border-radius: 2px; 144 | } 145 | .react-colorpicker-panel-preview input[type=color] { 146 | opacity: 0; 147 | } 148 | .react-colorpicker-panel-board { 149 | position: relative; 150 | font-size: 0; 151 | -webkit-user-select: none; 152 | -moz-user-select: none; 153 | -ms-user-select: none; 154 | user-select: none; 155 | } 156 | .react-colorpicker-panel-board span { 157 | position: absolute; 158 | border-radius: 10px; 159 | border: 1px solid #fff; 160 | width: 9px; 161 | height: 9px; 162 | left: -999px; 163 | top: -999px; 164 | box-shadow: 0 0 1px rgba(120, 120, 120, 0.7); 165 | z-index: 2; 166 | } 167 | .react-colorpicker-panel-board-hsv { 168 | width: 200px; 169 | height: 150px; 170 | position: relative; 171 | z-index: 1; 172 | border-radius: 2px; 173 | } 174 | .react-colorpicker-panel-board-value { 175 | border-radius: 2px; 176 | position: absolute; 177 | width: 100%; 178 | height: 100%; 179 | left: 0; 180 | top: 0; 181 | z-index: 2; 182 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9InJnYigwLDAsMCkiIHN0b3Atb3BhY2l0eT0iMCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 183 | background-image: linear-gradient(to bottom, transparent 0%, #000000 100%); 184 | } 185 | .react-colorpicker-panel-board-saturation { 186 | border-radius: 2px; 187 | position: absolute; 188 | width: 100%; 189 | height: 100%; 190 | left: 0; 191 | top: 0; 192 | z-index: 1; 193 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZmZmZmYiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0icmdiKDAsMCwwKSIgc3RvcC1vcGFjaXR5PSIwIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 194 | background-image: linear-gradient(to right, #ffffff 0%, transparent 100%); 195 | } 196 | .react-colorpicker-panel-board-handler { 197 | box-shadow: 0 0 2px #808080 inset; 198 | border-radius: 2px; 199 | cursor: crosshair; 200 | -webkit-user-select: none; 201 | -moz-user-select: none; 202 | -ms-user-select: none; 203 | user-select: none; 204 | position: absolute; 205 | top: 0; 206 | left: 0; 207 | width: 100%; 208 | height: 100%; 209 | z-index: 3; 210 | } 211 | .react-colorpicker-panel-ribbon { 212 | position: relative; 213 | height: 100%; 214 | border-radius: 2px; 215 | box-shadow: 0 0 2px #808080 inset; 216 | background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmZjAwMDAiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAlIiBzdG9wLWNvbG9yPSIjZmY5OTAwIiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjIwJSIgc3RvcC1jb2xvcj0iI2NkZmYwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSIzMCUiIHN0b3AtY29sb3I9IiMzNWZmMDAiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iNDAlIiBzdG9wLWNvbG9yPSIjMDBmZjY2IiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iIzAwZmZmZCIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiMwMDY2ZmYiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iNzAlIiBzdG9wLWNvbG9yPSIjMzIwMGZmIiBzdG9wLW9wYWNpdHk9IjEiLz48c3RvcCBvZmZzZXQ9IjgwJSIgc3RvcC1jb2xvcj0iI2NkMDBmZiIgc3RvcC1vcGFjaXR5PSIxIi8+PHN0b3Agb2Zmc2V0PSI5MCUiIHN0b3AtY29sb3I9IiNmZjAwOTkiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmMDAwMCIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=); 217 | background-image: linear-gradient(to right, #ff0000 0%, #ff9900 10%, #cdff00 20%, #35ff00 30%, #00ff66 40%, #00fffd 50%, #0066ff 60%, #3200ff 70%, #cd00ff 80%, #ff0099 90%, #ff0000 100%); 218 | } 219 | .react-colorpicker-panel-ribbon span { 220 | position: absolute; 221 | top: 0; 222 | height: 100%; 223 | width: 4px; 224 | border: 1px solid #000000; 225 | padding: 1px 0; 226 | margin-left: -2px; 227 | background-color: #fff; 228 | border-radius: 3px; 229 | } 230 | .react-colorpicker-panel-ribbon-handler { 231 | position: absolute; 232 | width: 104%; 233 | height: 100%; 234 | left: -2%; 235 | cursor: pointer; 236 | } 237 | .react-colorpicker-panel-alpha { 238 | position: relative; 239 | height: 100%; 240 | width: 100%; 241 | border-radius: 2px; 242 | background-image: url('data:image/png;base64,R0lGODdhCgAKAPAAAOXl5f///ywAAAAACgAKAEACEIQdqXt9GxyETrI279OIgwIAOw=='); 243 | background-repeat: repeat; 244 | -webkit-user-select: none; 245 | -moz-user-select: none; 246 | -ms-user-select: none; 247 | user-select: none; 248 | } 249 | .react-colorpicker-panel-alpha-bg { 250 | position: absolute; 251 | width: 100%; 252 | height: 100%; 253 | border-radius: 2px; 254 | box-shadow: 0 0 2px #808080 inset; 255 | } 256 | .react-colorpicker-panel-alpha span { 257 | position: absolute; 258 | top: 0; 259 | height: 100%; 260 | width: 4px; 261 | border: 1px solid #000000; 262 | padding: 1px 0; 263 | margin-left: -2px; 264 | background-color: #fff; 265 | border-radius: 3px; 266 | } 267 | .react-colorpicker-panel-alpha-handler { 268 | position: absolute; 269 | width: 104%; 270 | height: 100%; 271 | left: -2%; 272 | cursor: pointer; 273 | } 274 | .react-colorpicker-panel-params { 275 | font-size: 12px; 276 | } 277 | .react-colorpicker-panel-params-input { 278 | overflow: hidden; 279 | padding: 2px 0; 280 | } 281 | .react-colorpicker-panel-params input { 282 | -webkit-user-select: text; 283 | -moz-user-select: text; 284 | -ms-user-select: text; 285 | user-select: text; 286 | text-align: center; 287 | padding: 1px; 288 | margin: 0; 289 | float: left; 290 | border-radius: 2px; 291 | border: 1px solid #CACACA; 292 | font-family: 'Helvetica Neue', Helvetica, sans-serif; 293 | } 294 | .react-colorpicker-panel-params-hex { 295 | width: 52px; 296 | } 297 | .react-colorpicker-panel-params input[type=number] { 298 | margin-left: 5px; 299 | width: 32px; 300 | } 301 | .react-colorpicker-panel-params input[type=number]::-webkit-inner-spin-button { 302 | -webkit-appearance: none; 303 | } 304 | .react-colorpicker-panel-params-lable { 305 | padding: 2px 0; 306 | height: 22px; 307 | line-height: 18px; 308 | -webkit-user-select: none; 309 | -moz-user-select: none; 310 | -ms-user-select: none; 311 | user-select: none; 312 | } 313 | .react-colorpicker-panel-params-lable label { 314 | float: left; 315 | text-align: center; 316 | } 317 | .react-colorpicker-panel-params-lable-hex { 318 | width: 52px; 319 | } 320 | .react-colorpicker-panel-params-lable-number, 321 | .react-colorpicker-panel-params-lable-alpha { 322 | margin-left: 5px; 323 | width: 32px; 324 | } 325 | .react-colorpicker-panel-params-lable-number:hover { 326 | border-radius: 2px; 327 | background-color: #eee; 328 | box-shadow: 0 0 0 1px #ccc inset; 329 | cursor: pointer; 330 | } 331 | .react-colorpicker { 332 | font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif; 333 | } 334 | .react-colorpicker-picker { 335 | position: absolute; 336 | left: -9999px; 337 | top: -9999px; 338 | z-index: 1000; 339 | } 340 | .react-colorpicker-picker-slide-up-enter { 341 | -webkit-animation-duration: .3s; 342 | animation-duration: .3s; 343 | -webkit-animation-fill-mode: both; 344 | animation-fill-mode: both; 345 | -webkit-transform-origin: 0 0; 346 | -ms-transform-origin: 0 0; 347 | transform-origin: 0 0; 348 | display: block !important; 349 | opacity: 0; 350 | -webkit-animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 351 | animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 352 | -webkit-animation-play-state: paused; 353 | animation-play-state: paused; 354 | } 355 | .react-colorpicker-picker-slide-up-appear { 356 | -webkit-animation-duration: .3s; 357 | animation-duration: .3s; 358 | -webkit-animation-fill-mode: both; 359 | animation-fill-mode: both; 360 | -webkit-transform-origin: 0 0; 361 | -ms-transform-origin: 0 0; 362 | transform-origin: 0 0; 363 | display: block !important; 364 | opacity: 0; 365 | -webkit-animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 366 | animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); 367 | -webkit-animation-play-state: paused; 368 | animation-play-state: paused; 369 | } 370 | .react-colorpicker-picker-slide-up-leave { 371 | -webkit-animation-duration: .3s; 372 | animation-duration: .3s; 373 | -webkit-animation-fill-mode: both; 374 | animation-fill-mode: both; 375 | -webkit-transform-origin: 0 0; 376 | -ms-transform-origin: 0 0; 377 | transform-origin: 0 0; 378 | display: block !important; 379 | opacity: 1; 380 | -webkit-animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); 381 | animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); 382 | -webkit-animation-play-state: paused; 383 | animation-play-state: paused; 384 | } 385 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-bottomLeft, 386 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-bottomRight, 387 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-bottomLeft, 388 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-bottomRight { 389 | -webkit-animation-name: rcDropdownSlideUpIn; 390 | animation-name: rcDropdownSlideUpIn; 391 | -webkit-animation-play-state: running; 392 | animation-play-state: running; 393 | } 394 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-topLeft, 395 | .react-colorpicker-picker-slide-up-enter.react-colorpicker-picker-slide-up-enter-active.react-colorpicker-picker-placement-topRight, 396 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-topLeft, 397 | .react-colorpicker-picker-slide-up-appear.react-colorpicker-picker-slide-up-appear-active.react-colorpicker-picker-placement-topRight { 398 | -webkit-animation-name: rcDropdownSlideDownIn; 399 | animation-name: rcDropdownSlideDownIn; 400 | -webkit-animation-play-state: running; 401 | animation-play-state: running; 402 | } 403 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-bottomLeft, 404 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-bottomRight { 405 | -webkit-animation-name: rcDropdownSlideUpOut; 406 | animation-name: rcDropdownSlideUpOut; 407 | -webkit-animation-play-state: running; 408 | animation-play-state: running; 409 | } 410 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-topLeft, 411 | .react-colorpicker-picker-slide-up-leave.react-colorpicker-picker-slide-up-leave-active.react-colorpicker-picker-placement-topRight { 412 | -webkit-animation-name: rcDropdownSlideDownOut; 413 | animation-name: rcDropdownSlideDownOut; 414 | -webkit-animation-play-state: running; 415 | animation-play-state: running; 416 | } 417 | @-webkit-keyframes rcDropdownSlideUpIn { 418 | 0% { 419 | opacity: 0; 420 | -webkit-transform-origin: 0% 0%; 421 | transform-origin: 0% 0%; 422 | -webkit-transform: scaleY(0); 423 | transform: scaleY(0); 424 | } 425 | 100% { 426 | opacity: 1; 427 | -webkit-transform-origin: 0% 0%; 428 | transform-origin: 0% 0%; 429 | -webkit-transform: scaleY(1); 430 | transform: scaleY(1); 431 | } 432 | } 433 | @keyframes rcDropdownSlideUpIn { 434 | 0% { 435 | opacity: 0; 436 | -webkit-transform-origin: 0% 0%; 437 | transform-origin: 0% 0%; 438 | -webkit-transform: scaleY(0); 439 | transform: scaleY(0); 440 | } 441 | 100% { 442 | opacity: 1; 443 | -webkit-transform-origin: 0% 0%; 444 | transform-origin: 0% 0%; 445 | -webkit-transform: scaleY(1); 446 | transform: scaleY(1); 447 | } 448 | } 449 | @-webkit-keyframes rcDropdownSlideUpOut { 450 | 0% { 451 | opacity: 1; 452 | -webkit-transform-origin: 0% 0%; 453 | transform-origin: 0% 0%; 454 | -webkit-transform: scaleY(1); 455 | transform: scaleY(1); 456 | } 457 | 100% { 458 | opacity: 0; 459 | -webkit-transform-origin: 0% 0%; 460 | transform-origin: 0% 0%; 461 | -webkit-transform: scaleY(0); 462 | transform: scaleY(0); 463 | } 464 | } 465 | @keyframes rcDropdownSlideUpOut { 466 | 0% { 467 | opacity: 1; 468 | -webkit-transform-origin: 0% 0%; 469 | transform-origin: 0% 0%; 470 | -webkit-transform: scaleY(1); 471 | transform: scaleY(1); 472 | } 473 | 100% { 474 | opacity: 0; 475 | -webkit-transform-origin: 0% 0%; 476 | transform-origin: 0% 0%; 477 | -webkit-transform: scaleY(0); 478 | transform: scaleY(0); 479 | } 480 | } 481 | @-webkit-keyframes rcDropdownSlideDownIn { 482 | 0% { 483 | opacity: 0; 484 | -webkit-transform-origin: 100% 100%; 485 | transform-origin: 100% 100%; 486 | -webkit-transform: scaleY(0); 487 | transform: scaleY(0); 488 | } 489 | 100% { 490 | opacity: 1; 491 | -webkit-transform-origin: 100% 100%; 492 | transform-origin: 100% 100%; 493 | -webkit-transform: scaleY(1); 494 | transform: scaleY(1); 495 | } 496 | } 497 | @keyframes rcDropdownSlideDownIn { 498 | 0% { 499 | opacity: 0; 500 | -webkit-transform-origin: 100% 100%; 501 | transform-origin: 100% 100%; 502 | -webkit-transform: scaleY(0); 503 | transform: scaleY(0); 504 | } 505 | 100% { 506 | opacity: 1; 507 | -webkit-transform-origin: 100% 100%; 508 | transform-origin: 100% 100%; 509 | -webkit-transform: scaleY(1); 510 | transform: scaleY(1); 511 | } 512 | } 513 | @-webkit-keyframes rcDropdownSlideDownOut { 514 | 0% { 515 | opacity: 1; 516 | -webkit-transform-origin: 100% 100%; 517 | transform-origin: 100% 100%; 518 | -webkit-transform: scaleY(1); 519 | transform: scaleY(1); 520 | } 521 | 100% { 522 | opacity: 0; 523 | -webkit-transform-origin: 100% 100%; 524 | transform-origin: 100% 100%; 525 | -webkit-transform: scaleY(0); 526 | transform: scaleY(0); 527 | } 528 | } 529 | @keyframes rcDropdownSlideDownOut { 530 | 0% { 531 | opacity: 1; 532 | -webkit-transform-origin: 100% 100%; 533 | transform-origin: 100% 100%; 534 | -webkit-transform: scaleY(1); 535 | transform: scaleY(1); 536 | } 537 | 100% { 538 | opacity: 0; 539 | -webkit-transform-origin: 100% 100%; 540 | transform-origin: 100% 100%; 541 | -webkit-transform: scaleY(0); 542 | transform: scaleY(0); 543 | } 544 | } 545 | .gc-container { 546 | width: 100%; 547 | margin: 0 0; 548 | padding: 0 0; 549 | position: relative; 550 | } 551 | 552 | .gc-container .react-colorpicker-trigger { 553 | width: 20px; 554 | height: 20px; 555 | } 556 | 557 | .gc-container .gc-canvas { 558 | width: 100%; 559 | height: 20px; 560 | background-color: white; 561 | } 562 | .gc-container svg { 563 | position: absolute; 564 | top: 0px; 565 | left: 0px; 566 | } 567 | 568 | .gc-container canvas { 569 | position: absolute; 570 | top: 0px; 571 | left: 0px; 572 | } 573 | 574 | .gc-container .gc-handler { 575 | fill: white; 576 | stroke: gray; 577 | fill-opacity: 0.7; 578 | cursor: move; 579 | } 580 | 581 | .gc-container .gc-colorpicker { 582 | position: absolute; 583 | } 584 | 585 | .gc-colorpicker .remove-btn { 586 | position: relative; 587 | position: absolute; 588 | top: 15px; 589 | left: 4px; 590 | cursor: pointer; 591 | opacity: 0; 592 | } 593 | .gc-colorpicker:hover .remove-btn { 594 | opacity: 1; 595 | } 596 | .react-colorpicker { 597 | border: 1px solid #999; 598 | padding: 0px; 599 | border-radius: 2px; 600 | } 601 | 602 | .react-colorpicker-trigger { 603 | border: 0px; 604 | margin-bottom: -2px; 605 | } 606 | /* JSON Editor */ 607 | .jsonEditor * { 608 | vertical-align: top; 609 | } 610 | 611 | .jsonEditor > .jsonObject > .jsonChildren { 612 | margin-left: 0; 613 | border-left: 0; 614 | padding: 0; 615 | } 616 | 617 | .jsonChildren { 618 | margin-left: 30px; 619 | display: none; 620 | font-weight: normal; 621 | border-left: 1px dotted #ddd; 622 | } 623 | .jsonFlex .jsonChildren { 624 | margin-left: -30px; 625 | } 626 | .jsonCompound.open > .jsonChildren { 627 | display: block; 628 | } 629 | 630 | .jsonAdd { 631 | margin-left: 5px; 632 | } 633 | .jsonFlex .jsonField { 634 | display: -ms-flexbox; 635 | display: -webkit-flex; 636 | display: flex; 637 | -webkit-flex-direction: row; 638 | -ms-flex-direction: row; 639 | flex-direction: row; 640 | -webkit-flex-wrap: nowrap; 641 | -ms-flex-wrap: nowrap; 642 | flex-wrap: nowrap; 643 | -webkit-justify-content: flex-start; 644 | -ms-flex-pack: start; 645 | justify-content: flex-start; 646 | -webkit-align-content: stretch; 647 | -ms-flex-line-pack: stretch; 648 | align-content: stretch; 649 | } 650 | .jsonFieldAdder { 651 | margin-left: 13px; 652 | } 653 | .jsonFieldAdder select { 654 | margin-left: 5px; 655 | } 656 | .compoundToggle:before { 657 | content: '▸'; 658 | color: #333; 659 | line-height: 1em; 660 | margin-right: 3px; 661 | } 662 | .open > .compoundToggle:before { 663 | content: '▾'; 664 | } 665 | 666 | .jsonName { 667 | font-style: italic; 668 | position: relative; 669 | padding-left: 15px; 670 | white-space: nowrap; 671 | min-width:80px; 672 | } 673 | 674 | .jsonValue { 675 | position:relative; 676 | padding-left: 15px; 677 | flex-grow: 1; 678 | } 679 | 680 | .jsonValue > input, 681 | .jsonValue > textarea, 682 | .jsonValue > select { 683 | margin-bottom: 5px; 684 | } 685 | 686 | .jsonFlex input[type=text], 687 | .jsonFlex input[type=number], 688 | .jsonFlex input[type=password], 689 | .jsonFlex textarea { 690 | width: 100%; 691 | } 692 | 693 | .jsonRemove, .jsonFixed { 694 | visibility: hidden; 695 | margin-right: 5px; 696 | text-decoration: none; 697 | width: .6em; 698 | display: inline-block; 699 | position: absolute; 700 | left: 0; 701 | } 702 | 703 | .jsonName:hover > .jsonRemove { 704 | visibility: visible; 705 | } 706 | 707 | .jsonReset{ 708 | visibility: hidden; 709 | padding-left: 0px; 710 | text-decoration: none; 711 | width: .6em; 712 | display: inline-block; 713 | position: absolute; 714 | left: 0; 715 | } 716 | .jsonValue:hover > .jsonReset { 717 | visibility: visible; 718 | } 719 | 720 | .jsonBind{ 721 | visibility: hidden; 722 | padding-left: 10px; 723 | text-decoration: none; 724 | width: .6em; 725 | display: inline-block; 726 | position: absolute; 727 | left: 0; 728 | } 729 | .jsonValue:hover > .jsonBind { 730 | visibility: visible; 731 | } 732 | 733 | 734 | 735 | //pre { 736 | // background: #eee; 737 | // border: 1px solid #ddd; 738 | // padding: 5px; 739 | // //float: left; 740 | // width: 180px; 741 | // margin: 0 10px 0 0; 742 | //} 743 | 744 | .compoundToggle, .jsonString{ 745 | cursor: pointer; 746 | } 747 | 748 | .booleanField * { 749 | vertical-align: middle; 750 | } 751 | 752 | .booleanField input { 753 | margin-bottom: 0; 754 | } 755 | 756 | .jsonError > .jsonName, 757 | .jsonError .jsonErrorMsg { 758 | color: #f36; 759 | } 760 | 761 | .jsonError .jsonErrorMsg { 762 | margin: 0 0 10px 10px; 763 | font-size: 0.9em; 764 | vertical-align: bottom; 765 | display: block; 766 | } 767 | 768 | .jsonError > .jsonValue > input, 769 | .jsonError > .jsonValue > select, 770 | .jsonError > .jsonValue > textarea { 771 | background-color: #fff8f8; 772 | } 773 | 774 | .jsonNovalue { 775 | color: #999; 776 | font-style: italic; 777 | } 778 | 779 | .jsonGroup { 780 | margin-bottom: 10px; 781 | } 782 | 783 | .bindingEditor .jsonEditor{ 784 | display: inline-block; 785 | } 786 | 787 | 788 | .flex-container { 789 | display: flex; 790 | flex-direction:row; 791 | flex-wrap: wrap; 792 | justify-content: space-between; 793 | } 794 | .flex-item { 795 | 796 | margin: 10px; 797 | text-align: center; 798 | min-width:150px; 799 | 800 | .thumb{ 801 | background-color: gainsboro; 802 | border-radius: 4px; 803 | display: inline-block; 804 | &:hover{ 805 | border:1px solid cadetblue; 806 | } 807 | 808 | } 809 | span { 810 | vertical-align:middle; 811 | text-align: center; 812 | } 813 | .footer{ 814 | text-align: center; 815 | min-width:100%; 816 | } 817 | } 818 | -------------------------------------------------------------------------------- /example/src/excel.css: -------------------------------------------------------------------------------- 1 | .excel table { 2 | font-family: Calibri, 'Segoe UI', Thonburi, Arial, Verdana, sans-serif; 3 | font-size: 14px; 4 | border-spacing: 0; 5 | color: #000; 6 | border-width: 0 0 1px 1px; 7 | border-style: solid; 8 | border-color: #cacaca; 9 | table-layout: fixed; 10 | } 11 | .excel table:focus { 12 | outline: none; 13 | } 14 | .excel td, 15 | .excel th { 16 | position: relative; 17 | display: inline-block; 18 | box-sizing: border-box; 19 | width: 80px; 20 | height: 25px; 21 | margin: 0; 22 | padding: 0; 23 | border-width: 1px 1px 0 0; 24 | border-style: solid; 25 | border-color: #cacaca; 26 | overflow: hidden; 27 | text-overflow: ellipsis; 28 | white-space: nowrap; 29 | } 30 | .excel th { 31 | background-color: #f0f0f0; 32 | } 33 | .excel td div, 34 | .excel th div { 35 | height: 100%; 36 | } 37 | .excel td span, 38 | .excel th span { 39 | line-height: 15px; 40 | display: inline-block; 41 | width: calc(92%); 42 | height: calc(92%); 43 | padding: 4px; 44 | box-sizing: initial; 45 | } 46 | .excel td input { 47 | font-family: Calibri, 'Segoe UI', Thonburi, Arial, Verdana, sans-serif; 48 | font-size: 14px; 49 | position: absolute; 50 | z-index: 100; 51 | display: inline-block; 52 | width: calc(92%); 53 | height: calc(92%); 54 | margin: 0; 55 | padding: 4px; 56 | color: #000; 57 | border: none; 58 | background-color: #fff; 59 | } 60 | .excel td.selected { 61 | border: 2px solid #1e6337; 62 | } 63 | .excel td.selected span { 64 | padding: 3px 3px 3px 2px; 65 | } 66 | .excel input:focus, 67 | .excel select:focus, 68 | .excel textarea:focus, 69 | .excel button:focus { 70 | outline: none; 71 | width: 100%; 72 | height: 100%; 73 | } 74 | -------------------------------------------------------------------------------- /example/src/excel.less: -------------------------------------------------------------------------------- 1 | .excel { 2 | table { 3 | font-family: Calibri, 'Segoe UI', Thonburi, Arial, Verdana, sans-serif; 4 | font-size: 14px; 5 | 6 | border-spacing: 0; 7 | 8 | color: #000; 9 | border-width: 0 0 1px 1px; 10 | border-style: solid; 11 | border-color: #cacaca; 12 | table-layout: fixed; 13 | } 14 | 15 | table:focus { 16 | outline: none; 17 | } 18 | 19 | td, 20 | th { 21 | position: relative; 22 | 23 | display: inline-block; 24 | 25 | box-sizing: border-box; 26 | width: 80px; 27 | height: 25px; 28 | margin: 0; 29 | padding: 0; 30 | 31 | border-width: 1px 1px 0 0; 32 | border-style: solid; 33 | border-color: #cacaca; 34 | overflow: hidden; 35 | text-overflow: ellipsis; 36 | white-space: nowrap; 37 | } 38 | 39 | th { 40 | background-color: #f0f0f0; 41 | } 42 | 43 | td div, 44 | th div { 45 | height: 100%; 46 | } 47 | 48 | td span, th span { 49 | line-height: 15px; 50 | 51 | display: inline-block; 52 | 53 | width: calc(100% - 8px); 54 | height: calc(100% - 8px); 55 | padding: 4px; 56 | box-sizing: initial; 57 | } 58 | 59 | td input { 60 | font-family: Calibri, 'Segoe UI', Thonburi, Arial, Verdana, sans-serif; 61 | font-size: 14px; 62 | 63 | position: absolute; 64 | z-index: 100; 65 | 66 | display: inline-block; 67 | 68 | width: calc(100% - 8px); 69 | height: calc(100% - 8px); 70 | margin: 0; 71 | padding: 4px; 72 | 73 | color: #000; 74 | border: none; 75 | background-color: #fff; 76 | } 77 | 78 | td.selected { 79 | border: 2px solid #1e6337; 80 | } 81 | 82 | td.selected span { 83 | padding: 3px 3px 3px 2px; 84 | } 85 | 86 | input:focus, 87 | select:focus, 88 | textarea:focus, 89 | button:focus { 90 | outline: none; 91 | width: 100%; 92 | height: 100%; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /example/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | PropertyEditor 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |

PropertyEditor

26 |

View project on GitHub

27 | 28 |
29 |
30 | 31 |
32 | 35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var initGulpTasks = require('react-component-gulp-tasks'); 3 | 4 | /** 5 | * Tasks are added by the react-component-gulp-tasks package 6 | * 7 | * See https://github.com/JedWatson/react-component-gulp-tasks 8 | * for documentation. 9 | * 10 | * You can also add your own additional gulp tasks if you like. 11 | */ 12 | 13 | var taskConfig = { 14 | 15 | component: { 16 | name: 'PropertyEditor', 17 | dependencies: [ 18 | 'classnames', 19 | 'react', 20 | 'react-dom' 21 | ], 22 | lib: 'lib' 23 | }, 24 | 25 | example: { 26 | src: 'example/src', 27 | dist: 'example/dist', 28 | files: [ 29 | 'index.html', 30 | '.gitignore' 31 | ], 32 | scripts: [ 33 | 'example.js' 34 | ], 35 | less: [ 36 | 'example.less' 37 | ] 38 | } 39 | 40 | }; 41 | 42 | initGulpTasks(gulp, taskConfig); 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-property-editor", 3 | "version": "1.4.0", 4 | "description": "PropertyEditor", 5 | "main": "lib/PropertyEditor.js", 6 | "author": "Roman Samec", 7 | "homepage": "https://github.com/rsamec/react-property-editor", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/rsamec/react-property-editor.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/rsamec/react-property-editor/issues" 14 | }, 15 | "dependencies": { 16 | "classnames": "^2.1.2", 17 | "lodash": "^3.10.1", 18 | "react-colors-picker": "^2.3.0", 19 | "react-gradient-color-picker": "^0.1.3", 20 | "react-json-fork": "0.0.2", 21 | "react-overlays": "^0.5.4", 22 | "react-tinymce": "^0.4.0", 23 | "react-codemirror": "^0.2.6" 24 | }, 25 | "devDependencies": { 26 | "react-binding": "^0.7.3", 27 | "react-bootstrap": "^0.28.1", 28 | "babel-eslint": "^4.1.3", 29 | "eslint": "^1.6.0", 30 | "eslint-plugin-react": "^3.5.1", 31 | "gulp": "^3.9.0", 32 | "react-component-gulp-tasks": "^0.7.6" 33 | }, 34 | "peerDependencies": { 35 | "react": "^0.14.0", 36 | "react-dom": "^0.14.0" 37 | }, 38 | "browserify-shim": { 39 | "react": "global:React" 40 | }, 41 | "scripts": { 42 | "build": "gulp clean && NODE_ENV=production gulp build", 43 | "examples": "gulp dev:server", 44 | "lint": "eslint ./; true", 45 | "publish:site": "NODE_ENV=production gulp publish:examples", 46 | "release": "NODE_ENV=production gulp release", 47 | "start": "gulp dev", 48 | "test": "echo \"no tests yet\" && exit 0", 49 | "watch": "gulp watch:lib" 50 | }, 51 | "keywords": [ 52 | "react", 53 | "react-component" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /src/PropertyEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Json from 'react-json-fork'; 3 | import _ from 'lodash'; 4 | 5 | import ColorPickerEditor from './editors/ColorPickerEditor.js'; 6 | import HtmlEditor from './editors/HtmlEditor.js'; 7 | import CodeEditor from './editors/CodeEditor.js'; 8 | import PlainTextEditor from './editors/PlainTextEditor.js'; 9 | import PlainJsonEditor from './editors/PlainJsonEditor.js'; 10 | import JsonEditor from './editors/JsonEditor.js'; 11 | import FontEditor from './editors/FontEditor.js'; 12 | import BoxEditor from './editors/BoxEditor.js'; 13 | import BoxSizeEditor from './editors/BoxSizeEditor.js'; 14 | import BorderEditor from './editors/BorderEditor.js'; 15 | import PositionEditor from './editors/PositionEditor.js'; 16 | import BindingEditor from './editors/BindingEditor.js'; 17 | import BindingValueEditor from './editors/BindingValueEditor.js'; 18 | import BgEditor from './editors/BgEditor'; 19 | import GradientColorPicker from './editors/GradientColorPickerEditor'; 20 | import WidgetStyleEditor from './editors/WidgetStyleEditor'; 21 | import GridEditor from './editors/PlainJsonEditor'; 22 | import PageOptionsEditor from './editors/PageOptionsEditor'; 23 | 24 | import ModalStyles from './utils/ModalStyles.js'; 25 | 26 | // Register the type in react-json 27 | Json.registerType('colorPicker', ColorPickerEditor); 28 | Json.registerType('gradientColorPicker', GradientColorPicker); 29 | Json.registerType('htmlEditor', HtmlEditor); 30 | Json.registerType('codeEditor',CodeEditor); 31 | Json.registerType('textEditor',PlainTextEditor); 32 | Json.registerType('plainJsonEditor',PlainJsonEditor); 33 | Json.registerType('jsonEditor',JsonEditor); 34 | Json.registerType('fontEditor',FontEditor); 35 | Json.registerType('boxEditor',BoxEditor); 36 | Json.registerType('boxSizeEditor',BoxSizeEditor); 37 | Json.registerType('positionEditor',PositionEditor); 38 | Json.registerType('borderEditor',BorderEditor); 39 | Json.registerType('bindingEditor',BindingEditor); 40 | Json.registerType('bindingValueEditor',BindingValueEditor); 41 | Json.registerType('dataEditor',JsonEditor); 42 | Json.registerType('bgEditor',BgEditor); 43 | Json.registerType('widgetStyleEditor',WidgetStyleEditor); 44 | Json.registerType('gridEditor',GridEditor); 45 | Json.registerType('pageOptionsEditor',PageOptionsEditor); 46 | 47 | var defaultSettings = { 48 | form: true, 49 | fixedFields:true, 50 | adder:false, 51 | editing:true, 52 | fields:{ 53 | color:{type:'colorPicker'}, 54 | gradient:{type:'gradientColorPicker'}, 55 | fill:{type:'colorPicker'}, 56 | stroke:{type:'colorPicker'}, 57 | strokeWidth:{type:'number'}, 58 | width:{type:'number'}, 59 | height:{type:'number'}, 60 | html:{type:'htmlEditor'}, 61 | content:{type:'htmlEditor'}, 62 | data:{type:'bindingEditor'}, 63 | description:{type:'textEditor'}, 64 | font:{type:'fontEditor'}, 65 | border:{type:'borderEditor'}, 66 | style:{type:'positionEditor'}, 67 | margin:{type:'boxSizeEditor'}, 68 | padding:{type:'boxSizeEditor'}, 69 | box:{type:'boxEditor'}, 70 | binding:{type:'bindingEditor'}, 71 | value:{type:'bindingEditor'}, 72 | background:{type:'bgEditor'} 73 | } 74 | }; 75 | 76 | export default class PropertyEditor extends React.Component { 77 | propsChange(value){ 78 | this.props.onChange({props:value,binding:this.props.value.binding}); 79 | } 80 | bindingChange(value) { 81 | this.props.onChange({props: this.props.value.props, binding: value}); 82 | } 83 | render(){ 84 | var value = this.props.value; 85 | var props = value.props; 86 | var binding = value.binding; 87 | 88 | var settings = _.merge(_.cloneDeep(defaultSettings),this.props.settings); 89 | return ( ) 90 | } 91 | } 92 | PropertyEditor.registerType = function(type,editor){Json.registerType(type,editor)}; 93 | PropertyEditor.ModalStyles = function(){return ModalStyles}; 94 | -------------------------------------------------------------------------------- /src/editors/BgEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | import toEmptyProps from '../utils/toEmptyProps'; 6 | 7 | var settings = { 8 | form: true, 9 | fixedFields: true, 10 | adder: false, 11 | editing: true, 12 | fields:{ 13 | image:{type:'string'}, 14 | color:{type: 'colorPicker'}, 15 | gradient:{type: 'gradientColorPicker'}, 16 | size: {type: 'select', settings: {options: ['cover','contain','auto','leftHalf','rightHalf']}}, 17 | position:{type:'number'}, 18 | repeat: {type: 'select', settings: {options: ['repeat','repeat-x','repeat-y','no-repeat']}}, 19 | attachment: {type: 'select', settings: {options: ['scroll','fixed','local']}}, 20 | filter:{ 21 | fields:{ 22 | blur:{type:'number'}, 23 | brightness:{type:'number'}, 24 | contrast:{type:'number'}, 25 | grayscale:{type:'number'}, 26 | hueRotate:{type:'number'}, 27 | invert:{type:'number'}, 28 | opacity:{type:'number'}, 29 | saturate:{type:'number'}, 30 | sepia:{type:'number'} 31 | } 32 | } 33 | } 34 | }; 35 | export default class BgEditor extends React.Component { 36 | 37 | constructor(props) { 38 | super(props); 39 | this.state = {show: false}; 40 | } 41 | toogle(){ 42 | this.setState({show:!this.state.show}); 43 | } 44 | render() { 45 | var value = _.merge(toEmptyProps(settings),this.props.value); 46 | var text = '';//_.reduce(value,function(result,value,key){ return result+= " " + (value!==undefined?value:'--')},""); 47 | return ( 48 |
49 | {text} 50 | {this.state.show?:null} 51 |
52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/editors/BindingEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | var defaultValues = { 6 | path: undefined, 7 | converter: undefined, 8 | converterArgs:undefined, 9 | mode: 'OneWay' 10 | }; 11 | var settings = { 12 | form: true, 13 | fixedFields: true, 14 | adder: false, 15 | editing: true, 16 | fields: { 17 | mode: { 18 | type: 'select', settings: { 19 | editing:false, 20 | options: _.map(['OneWay', 'TwoWay'], function (key, value) { 21 | return {value: key, label: key}; 22 | }) 23 | } 24 | }, 25 | converter: { 26 | type: 'codeEditor' 27 | }, 28 | } 29 | }; 30 | 31 | export default class BindingEditor extends React.Component { 32 | 33 | constructor(props) { 34 | super(props); 35 | this.state = {show: false}; 36 | } 37 | toogle() { 38 | this.setState({show: !this.state.show}); 39 | } 40 | render() { 41 | var value = _.extend(_.clone(defaultValues), this.props.value); 42 | var textClass = !!value.path?"":"jsonNovalue"; 43 | var text = !!value.path?'= ' + value.path:"No value"; 44 | return ( 45 |
46 | {text} 47 | {this.state.show ? 48 | : null} 49 |
50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/editors/BindingValueEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | var defaultValues = { 6 | value:undefined, 7 | binding: { 8 | mode: 'OneWay', 9 | path: undefined, 10 | converter: undefined 11 | } 12 | }; 13 | var settings = { 14 | form: true, 15 | fixedFields: true, 16 | adder: false, 17 | editing: true, 18 | fields: { 19 | binding: { type:'bindingEditor'} 20 | } 21 | }; 22 | 23 | export default class BindingEditor extends React.Component { 24 | constructor(props) { 25 | super(props); 26 | this.state = {checked: props.value===undefined}; 27 | } 28 | checkedChanged(e){ 29 | this.setState({checked:e.target.checked}); 30 | } 31 | valueChanged(value){ 32 | this.props.onUpdated(value); 33 | } 34 | render() { 35 | var value = _.extend(_.clone(defaultValues), this.props.value); 36 | settings.hiddenFields = [this.state.checked?'value':'binding']; 37 | var valueType = this.props.settings && this.props.settings.type; 38 | if (valueType !== undefined) settings.fields.value = {type:valueType}; 39 | return ( 40 |
41 | 42 | 43 |
); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/editors/BorderEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | var defaultValues = { 6 | width:1, 7 | radius: 5, 8 | color:'#ffffff', 9 | style:'solid' 10 | }; 11 | var emptyValues = { 12 | width:undefined, 13 | radius: undefined, 14 | color:undefined, 15 | style:undefined 16 | } 17 | var settings = { 18 | form: true, 19 | fixedFields: true, 20 | adder: false, 21 | editing: true, 22 | fields:{ 23 | width:{type:'number'}, 24 | radius:{type:'number'}, 25 | color:{type:'colorPicker'}, 26 | style:{type:'select',settings:{options:['solid','dashed']}} 27 | } 28 | }; 29 | 30 | export default class BorderEditor extends React.Component { 31 | 32 | constructor(props) { 33 | super(props); 34 | this.state = {show: false}; 35 | } 36 | toogle(){ 37 | this.setState({show:!this.state.show}); 38 | } 39 | render() { 40 | var value = _.extend(_.clone(emptyValues),this.props.value); 41 | var text = _.reduce(value,function(result,value,key){ return result+= " " + (value!==undefined?value:'--')},""); 42 | return ( 43 |
44 | {text} 45 | {this.state.show?:null} 46 |
47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/editors/BoxEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | var defaultValues = { 6 | margin:{}, 7 | padding:{}, 8 | border:{}, 9 | content:{} 10 | }; 11 | var settings = { 12 | form: true, 13 | fixedFields: true, 14 | adder: false, 15 | editing: true, 16 | fields:{ 17 | margin:{type:'boxSizeEditor'}, 18 | padding:{type:'boxSizeEditor'}, 19 | border:{type:'borderEditor'}, 20 | content:{type:'htmlEditor'} 21 | } 22 | }; 23 | 24 | export default class BoxEditor extends React.Component { 25 | 26 | constructor(props) { 27 | super(props); 28 | this.state = {show: false}; 29 | } 30 | toogle(){ 31 | this.setState({show:!this.state.show}); 32 | } 33 | render() { 34 | var value = _.extend(_.clone(defaultValues),this.props.value); 35 | var text = _.reduce(value,function(result,value,key){ return result+= " " + (value!==undefined?value:'--')},""); 36 | return ( 37 |
38 | {text} 39 | {this.state.show?:null} 40 |
41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/editors/BoxSizeEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import _ from 'lodash'; 3 | import Json from 'react-json-fork'; 4 | 5 | var defaultValues = { 6 | top: undefined, 7 | right: undefined, 8 | bottom: undefined, 9 | left: undefined 10 | }; 11 | var settings = { 12 | form: true, 13 | fixedFields: true, 14 | adder: false, 15 | editing: true, 16 | fields: { 17 | top:{type:'number'}, 18 | right:{type:'number'}, 19 | bottom:{type:'number'}, 20 | left:{type:'number'} 21 | } 22 | }; 23 | 24 | export default class BoxSizeEditor extends React.Component { 25 | 26 | constructor(props) { 27 | super(props); 28 | this.state = {show: false}; 29 | } 30 | toogle(){ 31 | this.setState({show:!this.state.show}); 32 | } 33 | render() { 34 | var value = _.extend(_.clone(defaultValues),this.props.value); 35 | var text = _.reduce(value,function(result,value,key){ return result+= " " + (value!==undefined?value:'--')},""); 36 | return ( 37 |
38 | {text} 39 | {this.state.show?:null} 40 |
41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/editors/CodeEditor.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Modal} from 'react-overlays'; 3 | import ModalStyles from '../utils/ModalStyles.js'; 4 | import EmptyValue from '../utils/EmptyValue.js'; 5 | 6 | var _ = require('lodash'); 7 | var babel = require('babel-core'); 8 | var CodeMirror = require('react-codemirror'); 9 | var SyntaxHighLight = require('codemirror/mode/javascript/javascript'); 10 | 11 | 12 | export default class CodeEditor extends React.Component { 13 | 14 | //static propTypes = {onClose: PropTypes.func} 15 | constructor(props) { 16 | super(props); 17 | var code = this.props.value && this.props.value.code || ''; 18 | this.state = {show: false, value: code}; 19 | } 20 | 21 | close() { 22 | //var editor = React.findDOMNode(this.refs.editor); 23 | var codeToCompile = '(function() {' + this.state.value + '})();'; 24 | //var code = ReactTools.transform(codeToCompile,{harmony: true}); 25 | //var code = JSXTransformer.transform(codeToCompile,{harmony: true}).code; 26 | 27 | var result = babel.transform(codeToCompile, {}); 28 | var newValue = {code: this.state.value, compiled: result.code}; 29 | this.props.onUpdated(newValue); 30 | this.setState({showModal: false}); 31 | } 32 | open() { 33 | this.setState({showModal: true}); 34 | } 35 | 36 | handleChange(newCode) { 37 | this.setState({value: newCode}); 38 | } 39 | componentWillReceiveProps(nextProps) { 40 | this.setState({value: nextProps.value && nextProps.value.code || '' }); 41 | } 42 | render() { 43 | var codeEditor = React.createElement(CodeMirror, { 44 | value: this.state.value, 45 | onChange: this.handleChange.bind(this), 46 | options: { 47 | // style: {border: '1px solid black'}, 48 | // textAreaClassName: ['form-control'], 49 | // textAreaStyle: {minHeight: '10em'}, 50 | mode: 'javascript', 51 | theme: 'solarized', 52 | lineNumbers: true 53 | } 54 | }); 55 | 56 | //var codeEditor =