├── .babelrc ├── .eslintrc ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── HEAD ├── HISTORY.md ├── LICENSE ├── README.md ├── build ├── react-object-inspector.js └── react-object-inspector.map ├── example ├── App.js ├── bundle.js ├── index.html └── index.js ├── karma.conf.js ├── package.json ├── server.js ├── src ├── ObjectDescription.js ├── ObjectInspector.js ├── ObjectPreview.js └── objectStyles.js ├── test ├── index.js └── objectDescription-test.js ├── webpack.config.js └── webpack.prod.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "jsx": true, 4 | "modules": true 5 | }, 6 | "env": { 7 | "browser": true, 8 | "node": true 9 | }, 10 | "parser": "babel-eslint", 11 | "rules": { 12 | // "quotes": [2, "single"], 13 | // "strict": [2, "never"], 14 | "no-var": 0, 15 | "react/jsx-uses-react": 2, 16 | "react/jsx-uses-vars": 2, 17 | "react/react-in-jsx-scope": 2 18 | }, 19 | "plugins": [ 20 | "react" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .DS_Store 4 | jsconfig.json 5 | 6 | react-codemod 7 | dev 8 | lib 9 | gh-pages/ 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "newcap": false 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .eslintrc 2 | .gitignore 3 | .npmignore 4 | jsconfig.json 5 | 6 | react-codemod 7 | dev 8 | gh-pages 9 | 10 | build/ 11 | example 12 | server.js 13 | src/ 14 | webpack.config.js 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "5" 5 | script: 6 | - npm run lint 7 | - npm test 8 | - npm run build 9 | before_install: 10 | - "export DISPLAY=:99.0" 11 | - "sh -e /etc/init.d/xvfb start" -------------------------------------------------------------------------------- /HEAD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xyc/react-object-inspector/d76a18474524c3721809abb309032ce5af102aa6/HEAD -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 (11/26/2015) 2 | - issue #4: inline styles 3 | - issue #5: Initial expanded paths 4 | 5 | ## 0.1.6 (10/26/2015) 6 | - Upgrade to React 0.14 7 | 8 | ## 0.1.5 (09/10/2015) 9 | - Display dates in string form 10 | - Resolve issue #6 "undefined" is rendered as Object 11 | 12 | ## 0.1.0 (08/06/2015) 13 | - Initial release. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Xiaoyi Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [DEPRECATION WARNING] future development will be at https://github.com/xyc/react-inspector 2 | 3 | react-object-inspector 4 | ===================== 5 | 6 | [![build status](https://img.shields.io/travis/xyc/react-object-inspector/master.svg?style=flat-square)](https://travis-ci.org/xyc/react-object-inspector) 7 | [![npm version](https://img.shields.io/npm/v/react-object-inspector.svg?style=flat-square)](https://www.npmjs.com/package/react-object-inspector) 8 | [![npm downloads](https://img.shields.io/npm/dm/react-object-inspector.svg?style=flat-square)](https://www.npmjs.com/package/react-object-inspector) 9 | 10 | Simple object inspector made with [React](http://facebook.github.io/react/) styled similarly to [Chrome DevTools](https://developer.chrome.com/devtools). You can use this tool to inspect Javascript Objects as an alternative to `
JSON.stringify(data, null, 2)
`. Check out the playground [here](http://xyc.github.io/react-object-inspector/) 11 | 12 | ![](http://xyc.github.io/react-object-inspector/screenshot.png) 13 | 14 | Tree state is saved at root. If you click to expand some elements in the hierarchy, the state will be preserved after the element is unmounted. 15 | 16 | ### Install 17 | 18 | NPM: 19 | ```sh 20 | npm install react-object-inspector 21 | ``` 22 | 23 | Starting from 0.2.0, react-object-inspector uses inline styles and you don't need to include any additional CSS files. 24 | 25 | ## API 26 | #### <ObjectInspector /> 27 | The component accepts the following props: 28 | #### `data`: the Javascript object you would like to inspect 29 | 30 | #### `name`: specify the name of the root node, default to `undefined` 31 | 32 | #### `initialExpandedPaths`: an array containing all the paths that should be expanded when the component is initialized. 33 | - A path is a dot separated string like `root.foo.bar` 34 | - By default you can refer to root's path as `'root'`, or the name prop if name is defined 35 | - For example, `['root']` expands the first level nodes 36 | - `['myCustomName']` can also expand the first level nodes if the component is setup as ``. 37 | - `['root.foo.bar']` expands the path `root.foo.bar` if `root.foo.bar` is an existing property 38 | - You can use wildcard to expand all paths on a specific level 39 | - For example, to expand all first level and second level nodes, use `['root', 'root.*']` 40 | 41 | 42 | ### Usage 43 | ```js 44 | import ObjectInspector from 'react-object-inspector'; 45 | let data = { /* ... */ }; 46 | 47 | React.render( 48 | , 49 | document.getElementById('objectInspector') 50 | ); 51 | ``` 52 | One common usage is embedding this in a component's render() method to provide a view for its props/state. 53 | 54 | ### Install the example 55 | ```sh 56 | npm install && npm start 57 | ``` 58 | Open http://localhost:3000/example/index.html 59 | -------------------------------------------------------------------------------- /build/react-object-inspector.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(require("react")); 4 | else if(typeof define === 'function' && define.amd) 5 | define(["react"], factory); 6 | else if(typeof exports === 'object') 7 | exports["ObjectInspector"] = factory(require("react")); 8 | else 9 | root["ObjectInspector"] = factory(root["React"]); 10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ exports: {}, 25 | /******/ id: moduleId, 26 | /******/ loaded: false 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.loaded = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // __webpack_public_path__ 47 | /******/ __webpack_require__.p = "build/"; 48 | /******/ 49 | /******/ // Load entry module and return exports 50 | /******/ return __webpack_require__(0); 51 | /******/ }) 52 | /************************************************************************/ 53 | /******/ ([ 54 | /* 0 */ 55 | /***/ function(module, exports, __webpack_require__) { 56 | 57 | 'use strict'; 58 | 59 | Object.defineProperty(exports, '__esModule', { 60 | value: true 61 | }); 62 | 63 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 64 | 65 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 66 | 67 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 68 | 69 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 70 | 71 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 72 | 73 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 74 | 75 | var _react = __webpack_require__(1); 76 | 77 | var _react2 = _interopRequireDefault(_react); 78 | 79 | var _ObjectDescription = __webpack_require__(2); 80 | 81 | var _ObjectDescription2 = _interopRequireDefault(_ObjectDescription); 82 | 83 | var _ObjectPreview = __webpack_require__(4); 84 | 85 | var _ObjectPreview2 = _interopRequireDefault(_ObjectPreview); 86 | 87 | // Constants 88 | 89 | // Styles 90 | 91 | var _objectStyles = __webpack_require__(3); 92 | 93 | var _objectStyles2 = _interopRequireDefault(_objectStyles); 94 | 95 | var DEFAULT_ROOT_PATH = 'root'; 96 | var styles = { 97 | base: { 98 | fontFamily: 'Menlo, monospace', 99 | fontSize: '11px', 100 | lineHeight: '14px', 101 | cursor: 'default' 102 | }, 103 | propertyNodesContainer: { 104 | paddingLeft: '12px' 105 | }, 106 | unselectable: { 107 | WebkitTouchCallout: 'none', 108 | WebkitUserSelect: 'none', 109 | KhtmlUserSelect: 'none', 110 | MozUserSelect: 'none', 111 | msUserSelect: 'none', 112 | OUserSelect: 'none', 113 | userSelect: 'none' 114 | }, 115 | expandControl: { 116 | color: '#6e6e6e', 117 | fontSize: '10px', 118 | marginRight: '3px', 119 | whiteSpace: 'pre' 120 | }, 121 | property: { 122 | paddingTop: '2px' 123 | } 124 | }; 125 | 126 | var ObjectInspector = (function (_Component) { 127 | _inherits(ObjectInspector, _Component); 128 | 129 | _createClass(ObjectInspector, null, [{ 130 | key: 'defaultProps', 131 | // path is dot separated property names to reach the current node 132 | value: { 133 | name: void 0, 134 | data: undefined, 135 | initialExpandedPaths: undefined, 136 | depth: 0, 137 | path: DEFAULT_ROOT_PATH 138 | }, 139 | enumerable: true 140 | }]); 141 | 142 | function ObjectInspector(props) { 143 | var _this = this; 144 | 145 | _classCallCheck(this, ObjectInspector); 146 | 147 | _get(Object.getPrototypeOf(ObjectInspector.prototype), 'constructor', this).call(this, props); 148 | 149 | if (props.depth === 0) { 150 | this.state = { expandedPaths: {} }; 151 | this.state.expandedPaths[props.path] = false; 152 | 153 | // initialize expandedPaths with initialExpandedPaths 154 | if (typeof props.initialExpandedPaths !== 'undefined') { 155 | props.initialExpandedPaths.map(function (expandedPath) { 156 | if (typeof expandedPath === 'string') { 157 | (function () { 158 | var wildcardPathToPaths = function wildcardPathToPaths(curObject, curPath, i) { 159 | var WILDCARD = "*"; 160 | if (i === names.length) { 161 | paths.push(curPath); 162 | return; 163 | } 164 | var name = names[i]; 165 | if (i === 0) { 166 | if (name === props.name || name === DEFAULT_ROOT_PATH || name === WILDCARD) { 167 | wildcardPathToPaths(curObject, 'root', i + 1); 168 | } 169 | } else { 170 | if (name === WILDCARD) { 171 | for (var propertyName in curObject) { 172 | if (curObject.hasOwnProperty(propertyName)) { 173 | var propertyValue = curObject[propertyName]; 174 | if (ObjectInspector.isExpandable(propertyValue)) { 175 | wildcardPathToPaths(propertyValue, curPath + '.' + propertyName, i + 1); 176 | } else { 177 | continue; 178 | } 179 | } 180 | } 181 | } else { 182 | var propertyValue = curObject[name]; 183 | if (ObjectInspector.isExpandable(propertyValue)) { 184 | wildcardPathToPaths(propertyValue, curPath + '.' + name, i + 1); 185 | } 186 | } 187 | } 188 | }; 189 | 190 | var names = expandedPath.split('.'); // wildcard names 191 | var paths = []; 192 | 193 | wildcardPathToPaths(props.data, '', 0); 194 | 195 | paths.map(function (path) { 196 | _this.state.expandedPaths[path] = true; 197 | }); 198 | })(); 199 | } 200 | }); 201 | } 202 | } 203 | } 204 | 205 | _createClass(ObjectInspector, [{ 206 | key: 'getExpanded', 207 | value: function getExpanded(path) { 208 | var expandedPaths = this.state.expandedPaths; 209 | if (typeof expandedPaths[path] !== 'undefined') { 210 | return expandedPaths[path]; 211 | } 212 | return false; 213 | } 214 | }, { 215 | key: 'setExpanded', 216 | value: function setExpanded(path, expanded) { 217 | var expandedPaths = this.state.expandedPaths; 218 | expandedPaths[path] = expanded; 219 | this.setState({ expandedPaths: expandedPaths }); 220 | } 221 | }, { 222 | key: 'handleClick', 223 | value: function handleClick() { 224 | // console.log(this.props.data); 225 | if (ObjectInspector.isExpandable(this.props.data)) { 226 | if (this.props.depth > 0) { 227 | this.props.setExpanded(this.props.path, !this.props.getExpanded(this.props.path)); 228 | } else { 229 | this.setExpanded(this.props.path, !this.getExpanded(this.props.path)); 230 | } 231 | } 232 | } 233 | }, { 234 | key: 'componentWillMount', 235 | value: function componentWillMount() { 236 | if (typeof _react2['default'].initializeTouchEvents === 'function') { 237 | _react2['default'].initializeTouchEvents(true); 238 | } 239 | } 240 | }, { 241 | key: 'render', 242 | value: function render() { 243 | 244 | var data = this.props.data; 245 | var name = this.props.name; 246 | 247 | var setExpanded = this.props.depth === 0 ? this.setExpanded.bind(this) : this.props.setExpanded; 248 | var getExpanded = this.props.depth === 0 ? this.getExpanded.bind(this) : this.props.getExpanded; 249 | var expanded = getExpanded(this.props.path); 250 | 251 | var expandGlyph = ObjectInspector.isExpandable(data) ? expanded ? '▼' : '▶' : this.props.depth === 0 ? '' // unnamed root node 252 | : ' '; 253 | 254 | var propertyNodesContainer = undefined; 255 | if (expanded) { 256 | var propertyNodes = []; 257 | 258 | for (var propertyName in data) { 259 | var propertyValue = data[propertyName]; 260 | if (data.hasOwnProperty(propertyName)) { 261 | propertyNodes.push(_react2['default'].createElement(ObjectInspector, { getExpanded: getExpanded, 262 | setExpanded: setExpanded, 263 | path: this.props.path + '.' + propertyName, 264 | depth: this.props.depth + 1, 265 | key: propertyName, 266 | name: propertyName, 267 | data: propertyValue })); 268 | } 269 | } 270 | propertyNodesContainer = _react2['default'].createElement( 271 | 'div', 272 | { style: styles.propertyNodesContainer }, 273 | propertyNodes 274 | ); 275 | } 276 | 277 | return _react2['default'].createElement( 278 | 'div', 279 | { style: styles.base }, 280 | _react2['default'].createElement( 281 | 'span', 282 | { style: styles.property, onClick: this.handleClick.bind(this) }, 283 | _react2['default'].createElement( 284 | 'span', 285 | { style: _extends({}, styles.expandControl, styles.unselectable) }, 286 | expandGlyph 287 | ), 288 | (function () { 289 | if (typeof name !== 'undefined') { 290 | return _react2['default'].createElement( 291 | 'span', 292 | null, 293 | _react2['default'].createElement( 294 | 'span', 295 | { style: _objectStyles2['default'].name }, 296 | name 297 | ), 298 | _react2['default'].createElement( 299 | 'span', 300 | null, 301 | ': ' 302 | ), 303 | _react2['default'].createElement(_ObjectDescription2['default'], { object: data }) 304 | ); 305 | } else { 306 | return _react2['default'].createElement(_ObjectPreview2['default'], { object: data }); 307 | } 308 | })() 309 | ), 310 | propertyNodesContainer 311 | ); 312 | } 313 | }], [{ 314 | key: 'isExpandable', 315 | value: function isExpandable(data) { 316 | return typeof data === 'object' && data !== null && Object.keys(data).length > 0; 317 | } 318 | }]); 319 | 320 | return ObjectInspector; 321 | })(_react.Component); 322 | 323 | exports['default'] = ObjectInspector; 324 | module.exports = exports['default']; 325 | // initial paths of the nodes that are visible 326 | 327 | /***/ }, 328 | /* 1 */ 329 | /***/ function(module, exports) { 330 | 331 | module.exports = __WEBPACK_EXTERNAL_MODULE_1__; 332 | 333 | /***/ }, 334 | /* 2 */ 335 | /***/ function(module, exports, __webpack_require__) { 336 | 337 | 'use strict'; 338 | 339 | Object.defineProperty(exports, '__esModule', { 340 | value: true 341 | }); 342 | 343 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 344 | 345 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 346 | 347 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 348 | 349 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 350 | 351 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 352 | 353 | var _react = __webpack_require__(1); 354 | 355 | var _react2 = _interopRequireDefault(_react); 356 | 357 | // Styles 358 | 359 | var _objectStyles = __webpack_require__(3); 360 | 361 | var _objectStyles2 = _interopRequireDefault(_objectStyles); 362 | 363 | /** 364 | * A short description of the object 365 | */ 366 | 367 | var ObjectDescription = (function (_Component) { 368 | _inherits(ObjectDescription, _Component); 369 | 370 | function ObjectDescription() { 371 | _classCallCheck(this, ObjectDescription); 372 | 373 | _get(Object.getPrototypeOf(ObjectDescription.prototype), 'constructor', this).apply(this, arguments); 374 | } 375 | 376 | _createClass(ObjectDescription, [{ 377 | key: 'render', 378 | value: function render() { 379 | var object = this.props.object; 380 | switch (typeof object) { 381 | case 'number': 382 | return _react2['default'].createElement( 383 | 'span', 384 | { style: _objectStyles2['default'].value.number }, 385 | object 386 | ); 387 | case 'string': 388 | return _react2['default'].createElement( 389 | 'span', 390 | { style: _objectStyles2['default'].value.string }, 391 | '"', 392 | object, 393 | '"' 394 | ); 395 | case 'boolean': 396 | return _react2['default'].createElement( 397 | 'span', 398 | { style: _objectStyles2['default'].value.boolean }, 399 | String(object) 400 | ); 401 | case 'undefined': 402 | return _react2['default'].createElement( 403 | 'span', 404 | { style: _objectStyles2['default'].value.undefined }, 405 | 'undefined' 406 | ); 407 | case 'object': 408 | if (object === null) { 409 | return _react2['default'].createElement( 410 | 'span', 411 | { style: _objectStyles2['default'].value['null'] }, 412 | 'null' 413 | ); 414 | } 415 | if (object instanceof Date) { 416 | return _react2['default'].createElement( 417 | 'span', 418 | null, 419 | object.toString() 420 | ); 421 | } 422 | if (Array.isArray(object)) { 423 | return _react2['default'].createElement( 424 | 'span', 425 | null, 426 | 'Array[' + object.length + ']' 427 | ); 428 | } 429 | return _react2['default'].createElement( 430 | 'span', 431 | null, 432 | 'Object' 433 | ); 434 | case 'function': 435 | return _react2['default'].createElement( 436 | 'span', 437 | null, 438 | _react2['default'].createElement( 439 | 'span', 440 | { style: _objectStyles2['default'].value['function'].keyword }, 441 | 'function' 442 | ), 443 | _react2['default'].createElement( 444 | 'span', 445 | { style: _objectStyles2['default'].value['function'].name }, 446 | ' ', 447 | object.name, 448 | '()' 449 | ) 450 | ); 451 | case 'symbol': 452 | return _react2['default'].createElement( 453 | 'span', 454 | { style: _objectStyles2['default'].value.symbol }, 455 | 'Symbol()' 456 | ); 457 | default: 458 | return _react2['default'].createElement('span', null); 459 | } 460 | } 461 | }]); 462 | 463 | return ObjectDescription; 464 | })(_react.Component); 465 | 466 | exports['default'] = ObjectDescription; 467 | module.exports = exports['default']; 468 | 469 | /***/ }, 470 | /* 3 */ 471 | /***/ function(module, exports) { 472 | 473 | 'use strict'; 474 | 475 | Object.defineProperty(exports, '__esModule', { 476 | value: true 477 | }); 478 | exports['default'] = { 479 | name: { 480 | color: 'rgb(136, 19, 145)' 481 | }, 482 | value: { 483 | 'null': { 484 | color: 'rgb(128, 128, 128)' 485 | }, 486 | undefined: { 487 | color: 'rgb(128, 128, 128)' 488 | }, 489 | string: { 490 | color: 'rgb(196, 26, 22)' 491 | }, 492 | symbol: { 493 | color: 'rgb(196, 26, 22)' 494 | }, 495 | number: { 496 | color: 'rgb(28, 0, 207)' 497 | }, 498 | boolean: { 499 | color: 'rgb(28, 0, 207)' 500 | }, 501 | 'function': { 502 | keyword: { 503 | color: 'rgb(170, 13, 145)', 504 | fontStyle: 'italic' 505 | }, 506 | name: { 507 | fontStyle: 'italic' 508 | } 509 | } 510 | } 511 | }; 512 | module.exports = exports['default']; 513 | 514 | /***/ }, 515 | /* 4 */ 516 | /***/ function(module, exports, __webpack_require__) { 517 | 518 | 'use strict'; 519 | 520 | Object.defineProperty(exports, '__esModule', { 521 | value: true 522 | }); 523 | 524 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 525 | 526 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 527 | 528 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 529 | 530 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 531 | 532 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 533 | 534 | var _react = __webpack_require__(1); 535 | 536 | var _react2 = _interopRequireDefault(_react); 537 | 538 | var _ObjectDescription = __webpack_require__(2); 539 | 540 | var _ObjectDescription2 = _interopRequireDefault(_ObjectDescription); 541 | 542 | // Styles 543 | 544 | var _objectStyles = __webpack_require__(3); 545 | 546 | var _objectStyles2 = _interopRequireDefault(_objectStyles); 547 | 548 | var styles = { 549 | preview: { 550 | fontStyle: 'italic' 551 | } 552 | }; 553 | 554 | function intersperse(arr, sep) { 555 | if (arr.length === 0) { 556 | return []; 557 | } 558 | 559 | return arr.slice(1).reduce(function (xs, x, i) { 560 | return xs.concat([sep, x]); 561 | }, [arr[0]]); 562 | } 563 | 564 | /** 565 | * A preview of the object on root level node 566 | */ 567 | 568 | var ObjectPreview = (function (_Component) { 569 | _inherits(ObjectPreview, _Component); 570 | 571 | function ObjectPreview() { 572 | _classCallCheck(this, ObjectPreview); 573 | 574 | _get(Object.getPrototypeOf(ObjectPreview.prototype), 'constructor', this).apply(this, arguments); 575 | } 576 | 577 | _createClass(ObjectPreview, [{ 578 | key: 'render', 579 | value: function render() { 580 | var object = this.props.object; 581 | if (typeof object !== 'object' || object === null) { 582 | return _react2['default'].createElement(_ObjectDescription2['default'], { object: object }); 583 | } 584 | 585 | if (Array.isArray(object)) { 586 | return _react2['default'].createElement( 587 | 'span', 588 | { style: styles.preview }, 589 | '[', 590 | intersperse(object.map(function (element, index) { 591 | return _react2['default'].createElement(_ObjectDescription2['default'], { key: index, object: element }); 592 | }), ", "), 593 | ']' 594 | ); 595 | } else if (object instanceof Date) { 596 | return _react2['default'].createElement( 597 | 'span', 598 | null, 599 | object.toString() 600 | ); 601 | } else { 602 | var propertyNodes = []; 603 | for (var propertyName in object) { 604 | var propertyValue = object[propertyName]; 605 | if (object.hasOwnProperty(propertyName)) { 606 | var ellipsis = undefined; 607 | if (propertyNodes.length === this.props.maxProperties - 1 && Object.keys(object).length > this.props.maxProperties) { 608 | ellipsis = _react2['default'].createElement( 609 | 'span', 610 | { key: 'ellipsis' }, 611 | '…' 612 | ); 613 | } 614 | propertyNodes.push(_react2['default'].createElement( 615 | 'span', 616 | { key: propertyName }, 617 | _react2['default'].createElement( 618 | 'span', 619 | { style: _objectStyles2['default'].name }, 620 | propertyName 621 | ), 622 | ': ', 623 | _react2['default'].createElement(_ObjectDescription2['default'], { object: propertyValue }), 624 | ellipsis 625 | )); 626 | if (ellipsis) break; 627 | } 628 | } 629 | 630 | return _react2['default'].createElement( 631 | 'span', 632 | { style: styles.preview }, 633 | 'Object {', 634 | intersperse(propertyNodes, ", "), 635 | '}' 636 | ); 637 | } 638 | } 639 | }], [{ 640 | key: 'defaultProps', 641 | // maximum properties displayed in preview 642 | value: { 643 | maxProperties: 5 644 | }, 645 | enumerable: true 646 | }]); 647 | 648 | return ObjectPreview; 649 | })(_react.Component); 650 | 651 | exports['default'] = ObjectPreview; 652 | module.exports = exports['default']; 653 | 654 | /***/ } 655 | /******/ ]) 656 | }); 657 | ; 658 | //# sourceMappingURL=react-object-inspector.map -------------------------------------------------------------------------------- /build/react-object-inspector.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap b7d41dad084c183d9993","webpack:///./src/ObjectInspector.js","webpack:///external {\"root\":\"React\",\"commonjs2\":\"react\",\"commonjs\":\"react\",\"amd\":\"react\"}","webpack:///./src/ObjectDescription.js","webpack:///./src/objectStyles.js","webpack:///./src/ObjectPreview.js"],"names":[],"mappings":"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","file":"react-object-inspector.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"react\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"react\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ObjectInspector\"] = factory(require(\"react\"));\n\telse\n\t\troot[\"ObjectInspector\"] = factory(root[\"React\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"build/\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap b7d41dad084c183d9993\n **/","import React, { Component } from 'react';\n\nimport ObjectDescription from './ObjectDescription';\nimport ObjectPreview from './ObjectPreview';\n\n// Constants\nconst DEFAULT_ROOT_PATH='root';\n\n// Styles\nimport objectStyles from './objectStyles';\nconst styles = {\n base: {\n fontFamily: 'Menlo, monospace',\n fontSize: '11px',\n lineHeight: '14px',\n cursor: 'default',\n },\n propertyNodesContainer: {\n paddingLeft: '12px',\n },\n unselectable: {\n WebkitTouchCallout: 'none',\n WebkitUserSelect: 'none',\n KhtmlUserSelect: 'none',\n MozUserSelect: 'none',\n msUserSelect: 'none',\n OUserSelect: 'none',\n userSelect: 'none',\n },\n expandControl: {\n color: '#6e6e6e',\n fontSize: '10px',\n marginRight: '3px',\n whiteSpace: 'pre',\n },\n property: {\n paddingTop: '2px',\n },\n};\n\nexport default class ObjectInspector extends Component {\n\n propTypes: {\n name: PropTypes.string,\n data: PropTypes.object,\n initialExpandedPaths: PropTypes.array, // initial paths of the nodes that are visible\n depth: PropTypes.number.isRequired,\n path: PropTypes.string // path is dot separated property names to reach the current node\n }\n\n static defaultProps = {\n name: void 0,\n data: undefined,\n initialExpandedPaths: undefined,\n depth: 0,\n path: DEFAULT_ROOT_PATH\n }\n\n constructor(props) {\n super(props);\n\n if(props.depth === 0){\n this.state = {expandedPaths: {}};\n this.state.expandedPaths[props.path] = false;\n\n // initialize expandedPaths with initialExpandedPaths\n if(typeof props.initialExpandedPaths !== 'undefined'){\n props.initialExpandedPaths.map((expandedPath)=>{\n if(typeof expandedPath === 'string'){\n const names = expandedPath.split('.'); // wildcard names\n const paths = [];\n function wildcardPathToPaths(curObject, curPath, i){\n const WILDCARD = \"*\";\n if(i === names.length){\n paths.push(curPath);\n return;\n }\n const name = names[i];\n if(i === 0){\n if(name === props.name || name === DEFAULT_ROOT_PATH || name === WILDCARD){\n wildcardPathToPaths(curObject, 'root', i + 1);\n }\n }\n else{\n if(name === WILDCARD){\n for(const propertyName in curObject){\n if(curObject.hasOwnProperty(propertyName)){\n const propertyValue = curObject[propertyName];\n if(ObjectInspector.isExpandable(propertyValue)){\n wildcardPathToPaths(propertyValue, curPath + '.' + propertyName, i + 1);\n }\n else{\n continue;\n }\n }\n }\n }\n else{\n const propertyValue = curObject[name];\n if(ObjectInspector.isExpandable(propertyValue)){\n wildcardPathToPaths(propertyValue, curPath + '.' + name, i + 1);\n }\n }\n }\n }\n wildcardPathToPaths(props.data, '', 0);\n\n paths.map((path)=>{\n this.state.expandedPaths[path] = true;\n })\n }\n });\n }\n }\n }\n\n static isExpandable(data){\n return (typeof data === 'object' && data !== null && Object.keys(data).length > 0);\n }\n\n getExpanded(path){\n const expandedPaths = this.state.expandedPaths;\n if (typeof expandedPaths[path] !== 'undefined') {\n return expandedPaths[path];\n }\n return false;\n }\n\n setExpanded(path, expanded){\n const expandedPaths = this.state.expandedPaths;\n expandedPaths[path] = expanded;\n this.setState({expandedPaths: expandedPaths});\n }\n\n handleClick() {\n // console.log(this.props.data);\n if (ObjectInspector.isExpandable(this.props.data)) {\n if (this.props.depth > 0) {\n this.props.setExpanded(this.props.path, !this.props.getExpanded(this.props.path));\n }\n else{\n this.setExpanded(this.props.path, !this.getExpanded(this.props.path));\n }\n }\n }\n\n componentWillMount(){\n if (typeof React.initializeTouchEvents === 'function') {\n React.initializeTouchEvents(true);\n }\n }\n\n render() {\n\n const data = this.props.data;\n const name = this.props.name;\n\n const setExpanded = (this.props.depth === 0) ? (this.setExpanded.bind(this)) : this.props.setExpanded;\n const getExpanded = (this.props.depth === 0) ? (this.getExpanded.bind(this)) : this.props.getExpanded;\n const expanded = getExpanded(this.props.path);\n\n const expandGlyph = ObjectInspector.isExpandable(data) ? (expanded ? '▼'\n : '▶')\n : (this.props.depth === 0 ? '' // unnamed root node\n : ' ');\n\n let propertyNodesContainer;\n if(expanded){\n let propertyNodes = [];\n\n for(let propertyName in data){\n const propertyValue = data[propertyName];\n if(data.hasOwnProperty(propertyName)){\n propertyNodes.push();\n }\n }\n propertyNodesContainer = (
{propertyNodes}
);\n }\n\n return (\n
\n \n {expandGlyph}\n {(() => {\n if (typeof name !== 'undefined') {\n return (\n {name}\n : \n \n );\n }\n else{\n return ();\n }\n })()}\n \n {propertyNodesContainer}\n
\n );\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/ObjectInspector.js\n **/","module.exports = __WEBPACK_EXTERNAL_MODULE_1__;\n\n\n/*****************\n ** WEBPACK FOOTER\n ** external {\"root\":\"React\",\"commonjs2\":\"react\",\"commonjs\":\"react\",\"amd\":\"react\"}\n ** module id = 1\n ** module chunks = 0\n **/","import React, { Component } from 'react';\n\n// Styles\nimport objectStyles from './objectStyles';\n\n/**\n * A short description of the object\n */\nexport default class ObjectDescription extends Component{\n render() {\n const object = this.props.object;\n switch (typeof object){\n case 'number':\n return ({object});\n case 'string':\n return ("{object}");\n case 'boolean':\n return ({String(object)});\n case 'undefined':\n return (undefined);\n case 'object':\n if(object === null){\n return (null)\n }\n if(object instanceof Date){\n return ({object.toString()});\n }\n if(Array.isArray(object)){\n return ({`Array[${object.length}]`});\n }\n return (Object);\n case 'function':\n return (\n function\n  {object.name}()\n );\n case 'symbol':\n return (Symbol())\n default:\n return ();\n }\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/ObjectDescription.js\n **/","export default {\n name: {\n color: 'rgb(136, 19, 145)',\n },\n value: {\n null: {\n color: 'rgb(128, 128, 128)',\n },\n undefined: {\n color: 'rgb(128, 128, 128)',\n },\n string: {\n color: 'rgb(196, 26, 22)',\n },\n symbol: {\n color: 'rgb(196, 26, 22)',\n },\n number: {\n color: 'rgb(28, 0, 207)',\n },\n boolean: {\n color: 'rgb(28, 0, 207)',\n },\n function: {\n keyword: {\n color: 'rgb(170, 13, 145)',\n fontStyle: 'italic',\n },\n name: {\n fontStyle: 'italic',\n },\n },\n },\n};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/objectStyles.js\n **/","import React, { Component } from 'react';\n\nimport ObjectDescription from './ObjectDescription';\n\n// Styles\nimport objectStyles from './objectStyles';\nconst styles = {\n preview: {\n fontStyle: 'italic',\n }\n}\n\nfunction intersperse(arr, sep){\n if (arr.length === 0) {\n return [];\n }\n\n return arr.slice(1).reduce(function(xs, x, i) {\n return xs.concat([sep, x]);\n }, [arr[0]]);\n}\n\n/**\n * A preview of the object on root level node\n */\nexport default class ObjectPreview extends Component {\n propTypes: {\n maxProperties: PropTypes.number; // maximum properties displayed in preview\n }\n\n static defaultProps = {\n maxProperties: 5\n }\n\n render() {\n const object = this.props.object;\n if (typeof object !== 'object' || object === null) {\n return ();\n }\n\n if (Array.isArray(object)) {\n return [\n {intersperse(object.map(function(element, index){\n return ()\n }), \", \")}\n ];\n }\n else if (object instanceof Date) {\n return {object.toString()};\n }\n else {\n let propertyNodes = [];\n for(let propertyName in object){\n const propertyValue = object[propertyName];\n if(object.hasOwnProperty(propertyName)){\n let ellipsis;\n if (propertyNodes.length === (this.props.maxProperties - 1)\n && Object.keys(object).length > this.props.maxProperties) {\n ellipsis = ();\n }\n propertyNodes.push(\n \n {propertyName}\n : \n \n {ellipsis}\n \n );\n if(ellipsis)\n break;\n }\n }\n\n return (\n {'Object {'}\n {intersperse(propertyNodes, \", \")}\n {'}'}\n );\n }\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/ObjectPreview.js\n **/"],"sourceRoot":""} -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import ObjectInspector from '../src/ObjectInspector'; 4 | 5 | function testFunction(){ 6 | console.log("hello world"); 7 | } 8 | 9 | export default class App extends Component { 10 | 11 | render() { 12 | const testObject = { 13 | "id": 2, 14 | "name": "An ice sculpture", 15 | // "price": 12.50, 16 | "tags": ["cold", "ice"], 17 | "dimensions": { 18 | "length": 7.0, 19 | "width": 12.0, 20 | "height": 9.5 21 | }, 22 | "warehouseLocation": { 23 | "latitude": -78.75, 24 | "longitude": 20.4 25 | } 26 | }; 27 | 28 | const test2 = {"employees":[ 29 | {"firstName":"John", "lastName":"Doe", "fullTime": true}, 30 | {"firstName":"Anna", "lastName":"Smith"}, 31 | {"firstName":"Peter", "lastName":"Jones"} 32 | ]}; 33 | 34 | const test3 = { 35 | "a1": 1, 36 | "a2": "A2", 37 | "a3": true, 38 | "a4": undefined, 39 | "a5": { 40 | "a5-1": null, 41 | "a5-2": ["a5-2-1", "a5-2-2"], 42 | "a5-3": {} 43 | }, 44 | "a6": function(){ 45 | console.log("hello world") 46 | }, 47 | "a7": new Date("2005-04-03") 48 | }; 49 | 50 | const test4 = { 51 | "login": "defunkt", 52 | "id": 2, 53 | "avatar_url": "https://avatars.githubusercontent.com/u/2?v=3", 54 | "gravatar_id": "", 55 | "url": "https://api.github.com/users/defunkt", 56 | "html_url": "https://github.com/defunkt", 57 | "followers_url": "https://api.github.com/users/defunkt/followers", 58 | "following_url": "https://api.github.com/users/defunkt/following{/other_user}", 59 | "gists_url": "https://api.github.com/users/defunkt/gists{/gist_id}", 60 | "starred_url": "https://api.github.com/users/defunkt/starred{/owner}{/repo}", 61 | "subscriptions_url": "https://api.github.com/users/defunkt/subscriptions", 62 | "organizations_url": "https://api.github.com/users/defunkt/orgs", 63 | "repos_url": "https://api.github.com/users/defunkt/repos", 64 | "events_url": "https://api.github.com/users/defunkt/events{/privacy}", 65 | "received_events_url": "https://api.github.com/users/defunkt/received_events", 66 | "type": "User", 67 | "site_admin": true, 68 | "name": "Chris Wanstrath", 69 | "company": "GitHub", 70 | "blog": "http://chriswanstrath.com/", 71 | "location": "San Francisco", 72 | "email": "chris@github.com", 73 | "hireable": true, 74 | "bio": null, 75 | "public_repos": 108, 76 | "public_gists": 280, 77 | "followers": 14509, 78 | "following": 208, 79 | "created_at": "2007-10-20T05:24:19Z", 80 | "updated_at": "2015-08-03T18:05:52Z" 81 | }; 82 | 83 | const test5 = { 84 | "glossary": { 85 | "title": "example glossary", 86 | "GlossDiv": { 87 | "title": "S", 88 | "GlossList": { 89 | "GlossEntry": { 90 | "ID": "SGML", 91 | "SortAs": "SGML", 92 | "GlossTerm": "Standard Generalized Markup Language", 93 | "Acronym": "SGML", 94 | "Abbrev": "ISO 8879:1986", 95 | "GlossDef": { 96 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 97 | "GlossSeeAlso": ["GML", "XML"] 98 | }, 99 | "GlossSee": "markup" 100 | } 101 | } 102 | } 103 | } 104 | }; 105 | 106 | // { 107 | // "a": function(){ 108 | // // iife ajax call 109 | // } 110 | // } 111 | 112 | const testObjects = [undefined, testFunction, null, true, false, "testString", 42, NaN, Symbol('foo'), testObject, test2, test3, test4, test5, [], ["a"], ["a", 1], new Date()]; 113 | 114 | return ( 115 |
116 | {(() => { 117 | // https://facebook.github.io/react/tips/if-else-in-JSX.html 118 | return testObjects.map(function(object, index){ 119 | return ( 120 |
121 | 122 | 123 |
); 124 | }); 125 | })()} 126 |
127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sample App 4 | 5 | 6 | 7 |
8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sun Sep 13 2015 21:58:59 GMT-0700 (PDT) 3 | 4 | 5 | var path = require('path'); 6 | 7 | var webpackConfig = { 8 | module: { 9 | loaders: [ 10 | { 11 | test: /\.(js|jsx)$/, loader: 'babel', include: [path.resolve('./src'), path.resolve('./test')], 12 | }, 13 | ] 14 | } 15 | }; 16 | 17 | module.exports = function(config) { 18 | config.set({ 19 | 20 | // base path that will be used to resolve all patterns (eg. files, exclude) 21 | basePath: '', 22 | 23 | 24 | // frameworks to use 25 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 26 | frameworks: ['jasmine'], 27 | 28 | 29 | // list of files / patterns to load in the browser 30 | files: [ 31 | 'test/index.js' 32 | ], 33 | 34 | 35 | // list of files to exclude 36 | exclude: [ 37 | ], 38 | 39 | webpack: webpackConfig, 40 | webpackMiddleware: { 41 | noInfo: true 42 | }, 43 | 44 | // preprocess matching files before serving them to the browser 45 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 46 | preprocessors: { 47 | 'test/index.js': ['webpack'], // run the test bundle through webpack plugin 48 | }, 49 | 50 | plugins: [ 51 | 'karma-jasmine', 52 | 'karma-phantomjs-launcher', 53 | 'karma-firefox-launcher', 54 | require("karma-webpack") 55 | ], 56 | 57 | 58 | // test results reporter to use 59 | // possible values: 'dots', 'progress' 60 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 61 | reporters: ['progress'], 62 | 63 | 64 | // web server port 65 | port: 9876, 66 | 67 | 68 | // enable / disable colors in the output (reporters and logs) 69 | colors: true, 70 | 71 | 72 | // level of logging 73 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 74 | logLevel: config.LOG_INFO, 75 | 76 | 77 | // enable / disable watching file and executing tests whenever any file changes 78 | autoWatch: true, 79 | 80 | 81 | // start these browsers 82 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 83 | browsers: ['PhantomJS', 'Firefox'], 84 | 85 | 86 | // Continuous Integration mode 87 | // if true, Karma captures browsers, runs the tests and exits 88 | singleRun: false 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-object-inspector", 3 | "version": "0.2.1", 4 | "description": "Simple object inspector styled similarly to Chrome DevTools", 5 | "main": "lib/ObjectInspector.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "start": "node server.js", 11 | "build": "babel src --out-dir lib && webpack --config webpack.prod.config.js", 12 | "lint": "eslint lib", 13 | "test": "karma start ./karma.conf.js --single-run", 14 | "clean": "rimraf lib", 15 | "prerelease": "npm run lint && npm run test && npm run clean && npm run build", 16 | "release": "" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/xyc/react-object-inspector.git" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "reactjs", 25 | "object", 26 | "object-inspector", 27 | "object-inspector", 28 | "treeview", 29 | "tree-view", 30 | "tree", 31 | "view", 32 | "inspector", 33 | "react-component", 34 | "ui" 35 | ], 36 | "author": "Xiaoyi Chen (http://github.com/xyc)", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/xyc/react-object-inspector/issues" 40 | }, 41 | "homepage": "https://github.com/xyc/react-object-inspector", 42 | "devDependencies": { 43 | "babel": "^5.5.8", 44 | "babel-core": "^5.6.18", 45 | "babel-eslint": "^4.1.0", 46 | "babel-loader": "^5.1.4", 47 | "core-js": "^1.1.4", 48 | "css-loader": "^0.23.0", 49 | "es5-shim": "^4.2.0", 50 | "eslint": "^1.7.1", 51 | "eslint-plugin-react": "^2.3.0", 52 | "jasmine-core": "^2.3.4", 53 | "karma": "^0.13.9", 54 | "karma-firefox-launcher": "^0.1.6", 55 | "karma-jasmine": "^0.3.6", 56 | "karma-phantomjs-launcher": "^0.2.1", 57 | "karma-webpack": "^1.7.0", 58 | "phantomjs": "^1.9.18", 59 | "react": "^0.14.2", 60 | "react-addons-test-utils": "^0.14.2", 61 | "react-dom": "^0.14.2", 62 | "react-hot-loader": "^1.2.7", 63 | "style-loader": "^0.13.0", 64 | "rimraf": "^2.4.4", 65 | "webpack": "^1.9.6", 66 | "webpack-dev-server": "^1.8.2" 67 | }, 68 | "peerDependencies": { 69 | "react": "^0.14.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | 5 | new WebpackDevServer(webpack(config), { 6 | publicPath: config.output.publicPath, 7 | hot: true, 8 | historyApiFallback: true 9 | }).listen(3000, 'localhost', function (err, result) { 10 | if (err) { 11 | console.log(err); 12 | } 13 | 14 | console.log('Listening at localhost:3000'); 15 | }); 16 | -------------------------------------------------------------------------------- /src/ObjectDescription.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | // Styles 4 | import objectStyles from './objectStyles'; 5 | 6 | /** 7 | * A short description of the object 8 | */ 9 | export default class ObjectDescription extends Component{ 10 | render() { 11 | const object = this.props.object; 12 | switch (typeof object){ 13 | case 'number': 14 | return ({object}); 15 | case 'string': 16 | return ("{object}"); 17 | case 'boolean': 18 | return ({String(object)}); 19 | case 'undefined': 20 | return (undefined); 21 | case 'object': 22 | if(object === null){ 23 | return (null) 24 | } 25 | if(object instanceof Date){ 26 | return ({object.toString()}); 27 | } 28 | if(Array.isArray(object)){ 29 | return ({`Array[${object.length}]`}); 30 | } 31 | return (Object); 32 | case 'function': 33 | return ( 34 | function 35 |  {object.name}() 36 | ); 37 | case 'symbol': 38 | return (Symbol()) 39 | default: 40 | return (); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ObjectInspector.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import ObjectDescription from './ObjectDescription'; 4 | import ObjectPreview from './ObjectPreview'; 5 | 6 | // Constants 7 | const DEFAULT_ROOT_PATH='root'; 8 | 9 | // Styles 10 | import objectStyles from './objectStyles'; 11 | const styles = { 12 | base: { 13 | fontFamily: 'Menlo, monospace', 14 | fontSize: '11px', 15 | lineHeight: '14px', 16 | cursor: 'default', 17 | }, 18 | propertyNodesContainer: { 19 | paddingLeft: '12px', 20 | }, 21 | unselectable: { 22 | WebkitTouchCallout: 'none', 23 | WebkitUserSelect: 'none', 24 | KhtmlUserSelect: 'none', 25 | MozUserSelect: 'none', 26 | msUserSelect: 'none', 27 | OUserSelect: 'none', 28 | userSelect: 'none', 29 | }, 30 | expandControl: { 31 | color: '#6e6e6e', 32 | fontSize: '10px', 33 | marginRight: '3px', 34 | whiteSpace: 'pre', 35 | }, 36 | property: { 37 | paddingTop: '2px', 38 | }, 39 | }; 40 | 41 | export default class ObjectInspector extends Component { 42 | 43 | propTypes: { 44 | name: PropTypes.string, 45 | data: PropTypes.object, 46 | initialExpandedPaths: PropTypes.array, // initial paths of the nodes that are visible 47 | depth: PropTypes.number.isRequired, 48 | path: PropTypes.string // path is dot separated property names to reach the current node 49 | } 50 | 51 | static defaultProps = { 52 | name: void 0, 53 | data: undefined, 54 | initialExpandedPaths: undefined, 55 | depth: 0, 56 | path: DEFAULT_ROOT_PATH 57 | } 58 | 59 | constructor(props) { 60 | super(props); 61 | 62 | if(props.depth === 0){ 63 | this.state = {expandedPaths: {}}; 64 | this.state.expandedPaths[props.path] = false; 65 | 66 | // initialize expandedPaths with initialExpandedPaths 67 | if(typeof props.initialExpandedPaths !== 'undefined'){ 68 | props.initialExpandedPaths.map((expandedPath)=>{ 69 | if(typeof expandedPath === 'string'){ 70 | const names = expandedPath.split('.'); // wildcard names 71 | const paths = []; 72 | function wildcardPathToPaths(curObject, curPath, i){ 73 | const WILDCARD = "*"; 74 | if(i === names.length){ 75 | paths.push(curPath); 76 | return; 77 | } 78 | const name = names[i]; 79 | if(i === 0){ 80 | if(name === props.name || name === DEFAULT_ROOT_PATH || name === WILDCARD){ 81 | wildcardPathToPaths(curObject, 'root', i + 1); 82 | } 83 | } 84 | else{ 85 | if(name === WILDCARD){ 86 | for(const propertyName in curObject){ 87 | if(curObject.hasOwnProperty(propertyName)){ 88 | const propertyValue = curObject[propertyName]; 89 | if(ObjectInspector.isExpandable(propertyValue)){ 90 | wildcardPathToPaths(propertyValue, curPath + '.' + propertyName, i + 1); 91 | } 92 | else{ 93 | continue; 94 | } 95 | } 96 | } 97 | } 98 | else{ 99 | const propertyValue = curObject[name]; 100 | if(ObjectInspector.isExpandable(propertyValue)){ 101 | wildcardPathToPaths(propertyValue, curPath + '.' + name, i + 1); 102 | } 103 | } 104 | } 105 | } 106 | wildcardPathToPaths(props.data, '', 0); 107 | 108 | paths.map((path)=>{ 109 | this.state.expandedPaths[path] = true; 110 | }) 111 | } 112 | }); 113 | } 114 | } 115 | } 116 | 117 | static isExpandable(data){ 118 | return (typeof data === 'object' && data !== null && Object.keys(data).length > 0); 119 | } 120 | 121 | getExpanded(path){ 122 | const expandedPaths = this.state.expandedPaths; 123 | if (typeof expandedPaths[path] !== 'undefined') { 124 | return expandedPaths[path]; 125 | } 126 | return false; 127 | } 128 | 129 | setExpanded(path, expanded){ 130 | const expandedPaths = this.state.expandedPaths; 131 | expandedPaths[path] = expanded; 132 | this.setState({expandedPaths: expandedPaths}); 133 | } 134 | 135 | handleClick() { 136 | // console.log(this.props.data); 137 | if (ObjectInspector.isExpandable(this.props.data)) { 138 | if (this.props.depth > 0) { 139 | this.props.setExpanded(this.props.path, !this.props.getExpanded(this.props.path)); 140 | } 141 | else{ 142 | this.setExpanded(this.props.path, !this.getExpanded(this.props.path)); 143 | } 144 | } 145 | } 146 | 147 | componentWillMount(){ 148 | if (typeof React.initializeTouchEvents === 'function') { 149 | React.initializeTouchEvents(true); 150 | } 151 | } 152 | 153 | render() { 154 | 155 | const data = this.props.data; 156 | const name = this.props.name; 157 | 158 | const setExpanded = (this.props.depth === 0) ? (this.setExpanded.bind(this)) : this.props.setExpanded; 159 | const getExpanded = (this.props.depth === 0) ? (this.getExpanded.bind(this)) : this.props.getExpanded; 160 | const expanded = getExpanded(this.props.path); 161 | 162 | const expandGlyph = ObjectInspector.isExpandable(data) ? (expanded ? '▼' 163 | : '▶') 164 | : (this.props.depth === 0 ? '' // unnamed root node 165 | : ' '); 166 | 167 | let propertyNodesContainer; 168 | if(expanded){ 169 | let propertyNodes = []; 170 | 171 | for(let propertyName in data){ 172 | const propertyValue = data[propertyName]; 173 | if(data.hasOwnProperty(propertyName)){ 174 | propertyNodes.push(); 181 | } 182 | } 183 | propertyNodesContainer = (
{propertyNodes}
); 184 | } 185 | 186 | return ( 187 |
188 | 189 | {expandGlyph} 190 | {(() => { 191 | if (typeof name !== 'undefined') { 192 | return ( 193 | {name} 194 | : 195 | 196 | ); 197 | } 198 | else{ 199 | return (); 200 | } 201 | })()} 202 | 203 | {propertyNodesContainer} 204 |
205 | ); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/ObjectPreview.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import ObjectDescription from './ObjectDescription'; 4 | 5 | // Styles 6 | import objectStyles from './objectStyles'; 7 | const styles = { 8 | preview: { 9 | fontStyle: 'italic', 10 | } 11 | } 12 | 13 | function intersperse(arr, sep){ 14 | if (arr.length === 0) { 15 | return []; 16 | } 17 | 18 | return arr.slice(1).reduce(function(xs, x, i) { 19 | return xs.concat([sep, x]); 20 | }, [arr[0]]); 21 | } 22 | 23 | /** 24 | * A preview of the object on root level node 25 | */ 26 | export default class ObjectPreview extends Component { 27 | propTypes: { 28 | maxProperties: PropTypes.number; // maximum properties displayed in preview 29 | } 30 | 31 | static defaultProps = { 32 | maxProperties: 5 33 | } 34 | 35 | render() { 36 | const object = this.props.object; 37 | if (typeof object !== 'object' || object === null) { 38 | return (); 39 | } 40 | 41 | if (Array.isArray(object)) { 42 | return [ 43 | {intersperse(object.map(function(element, index){ 44 | return () 45 | }), ", ")} 46 | ]; 47 | } 48 | else if (object instanceof Date) { 49 | return {object.toString()}; 50 | } 51 | else { 52 | let propertyNodes = []; 53 | for(let propertyName in object){ 54 | const propertyValue = object[propertyName]; 55 | if(object.hasOwnProperty(propertyName)){ 56 | let ellipsis; 57 | if (propertyNodes.length === (this.props.maxProperties - 1) 58 | && Object.keys(object).length > this.props.maxProperties) { 59 | ellipsis = (); 60 | } 61 | propertyNodes.push( 62 | 63 | {propertyName} 64 | :  65 | 66 | {ellipsis} 67 | 68 | ); 69 | if(ellipsis) 70 | break; 71 | } 72 | } 73 | 74 | return ( 75 | {'Object {'} 76 | {intersperse(propertyNodes, ", ")} 77 | {'}'} 78 | ); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/objectStyles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: { 3 | color: 'rgb(136, 19, 145)', 4 | }, 5 | value: { 6 | null: { 7 | color: 'rgb(128, 128, 128)', 8 | }, 9 | undefined: { 10 | color: 'rgb(128, 128, 128)', 11 | }, 12 | string: { 13 | color: 'rgb(196, 26, 22)', 14 | }, 15 | symbol: { 16 | color: 'rgb(196, 26, 22)', 17 | }, 18 | number: { 19 | color: 'rgb(28, 0, 207)', 20 | }, 21 | boolean: { 22 | color: 'rgb(28, 0, 207)', 23 | }, 24 | function: { 25 | keyword: { 26 | color: 'rgb(170, 13, 145)', 27 | fontStyle: 'italic', 28 | }, 29 | name: { 30 | fontStyle: 'italic', 31 | }, 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | // http://kentor.me/posts/testing-react-and-flux-applications-with-karma-and-webpack/ 2 | // ES5 shims for Function.prototype.bind, Object.prototype.keys, etc. 3 | // require('core-js/es5'); 4 | require('es5-shim'); 5 | 6 | var context = require.context('./', true, /-test\.js$/); 7 | context.keys().forEach(context); 8 | -------------------------------------------------------------------------------- /test/objectDescription-test.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import TestUtils from 'react-addons-test-utils'; 4 | 5 | const ObjectDescription = require('../src/ObjectDescription'); 6 | 7 | describe('ObjectDescription', () => { 8 | 9 | beforeEach(() => { 10 | 11 | }); 12 | 13 | it('should render number', () => { 14 | const desc = TestUtils.renderIntoDocument(); 15 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 16 | 17 | expect(ReactDOM.findDOMNode(span).textContent).toBe("0"); 18 | }); 19 | 20 | it('should render string with quotes', () => { 21 | const desc = TestUtils.renderIntoDocument(); 22 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 23 | 24 | expect(ReactDOM.findDOMNode(span).textContent).toBe("\"octocat\""); 25 | }); 26 | 27 | it('should render boolean', () => { 28 | for(let value of [true, false]){ 29 | const desc = TestUtils.renderIntoDocument(); 30 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 31 | 32 | expect(ReactDOM.findDOMNode(span).textContent).toBe(value.toString()); 33 | } 34 | }); 35 | 36 | it('should render undefined', () => { 37 | const desc = TestUtils.renderIntoDocument(); 38 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 39 | 40 | expect(ReactDOM.findDOMNode(span).textContent).toBe("undefined"); 41 | }); 42 | 43 | it('shoudl render null', () => { 44 | const desc = TestUtils.renderIntoDocument(); 45 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 46 | 47 | expect(ReactDOM.findDOMNode(span).textContent).toBe("null"); 48 | }); 49 | 50 | it('should display date correctly', () => { 51 | const dateString = 'December 17, 1995 03:24:00'; 52 | const date = new Date(dateString); 53 | const desc = TestUtils.renderIntoDocument(); 54 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 55 | 56 | expect(ReactDOM.findDOMNode(span).textContent).toBe((new Date(dateString)).toString()); 57 | }); 58 | 59 | it('should render array with length information', () => { 60 | const desc = TestUtils.renderIntoDocument(); 61 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 62 | 63 | expect(ReactDOM.findDOMNode(span).textContent).toBe("Array[5]"); 64 | }); 65 | 66 | it('should render an empty object', () => { 67 | const desc = TestUtils.renderIntoDocument(); 68 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 69 | 70 | expect(ReactDOM.findDOMNode(span).textContent).toBe("Object"); 71 | }); 72 | 73 | it('should render a simple object', () => { 74 | const desc = TestUtils.renderIntoDocument(); 75 | const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 76 | 77 | expect(ReactDOM.findDOMNode(span).textContent).toBe("Object"); 78 | }); 79 | 80 | it('should render an anonymous function', () => { 81 | const desc = TestUtils.renderIntoDocument(); 82 | const spans = TestUtils.scryRenderedDOMComponentsWithTag(desc, 'span'); 83 | 84 | expect(spans.length).toBe(3); 85 | }); 86 | 87 | it('should render a named function', () => { 88 | const desc = TestUtils.renderIntoDocument(); 89 | const spans = TestUtils.scryRenderedDOMComponentsWithTag(desc, 'span'); 90 | 91 | expect(spans.length).toBe(3); 92 | expect(ReactDOM.findDOMNode(spans[0]).textContent).toBe("function\xa0id()"); 93 | }); 94 | 95 | // Need phantomjs 2 96 | // it('symbol', () => { 97 | // const desc = TestUtils.renderIntoDocument(); 98 | // const span = TestUtils.findRenderedDOMComponentWithTag(desc, 'span'); 99 | // 100 | // expect(React.findDOMNode(span).textContent).toBe("Symbol()"); 101 | // }); 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | devtool: 'eval', 6 | entry: [ 7 | 'webpack-dev-server/client?http://localhost:3000', 8 | 'webpack/hot/only-dev-server', 9 | './example/index.js' 10 | ], 11 | output: { 12 | path: path.join(__dirname, 'example'), 13 | filename: 'bundle.js', 14 | publicPath: '/static/' 15 | }, 16 | plugins: [ 17 | new webpack.HotModuleReplacementPlugin(), 18 | new webpack.NoErrorsPlugin() 19 | ], 20 | resolve: { 21 | extensions: ['', '.js', '.jsx', '.css'] 22 | }, 23 | module: { 24 | loaders: [ 25 | { 26 | test: /\.(js|jsx)$/, 27 | loaders: ['react-hot', 'babel'], 28 | include: [path.join(__dirname, 'src'), path.join(__dirname, 'example')] 29 | }, 30 | ] 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var config = { 4 | devtool: 'sourcemap', 5 | entry: { 6 | index: './src/ObjectInspector.js', 7 | }, 8 | output: { 9 | path: path.join(__dirname, 'build'), 10 | publicPath: 'build/', 11 | filename: 'react-object-inspector.js', 12 | sourceMapFilename: 'react-object-inspector.map', 13 | library: 'ObjectInspector', 14 | libraryTarget: 'umd', 15 | }, 16 | module: { 17 | loaders: [{ 18 | test: /\.(js|jsx)/, 19 | loader: 'babel', 20 | }, 21 | ], 22 | }, 23 | plugins: [], 24 | resolve: { 25 | extensions: ['', '.js', '.jsx'], 26 | }, 27 | externals: { 28 | 'react': { 29 | root: 'React', 30 | commonjs2: 'react', 31 | commonjs: 'react', 32 | amd: 'react', 33 | }, 34 | }, 35 | }; 36 | 37 | module.exports = config; 38 | --------------------------------------------------------------------------------