├── .babelrc
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── assets
└── demo.gif
├── dist
└── index.html
├── package.json
├── src
├── devTools.jsx
└── index.jsx
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .hz/
2 | dist/bundle.js
3 | node_modules/
4 | rethinkdb_data/
5 | *.log
6 | index.js
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .hz
2 | dist
3 | rethinkdb_data
4 | src
5 | .babelrc
6 | webpack.config.js
7 | *.log
8 | assets
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Ryan Delaney
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 | # Horizon Devtools
2 |
3 | > A better dev experience for Horizon users
4 |
5 | ## Demo
6 |
7 | 
8 |
9 | ## Installation
10 |
11 | ```
12 | npm install --save horizon-devtools
13 | ```
14 |
15 | ## Usage
16 |
17 | ```js
18 | // Require the dependencies
19 | import React from 'react'
20 | import ReactDOM from 'react-dom'
21 | import { createDevTools } from 'horizon-devtools'
22 | import App from './App'
23 |
24 | // Create your horizon instance
25 | let horizon = Horizon()
26 | horizon.connect()
27 |
28 | // Run the `createDevTools` function on your horizon instance
29 | // This returns a `DevTools` component to render into your react app
30 | let DevTools = createDevTools(horizon)
31 |
32 | // Render your app!
33 | ReactDOM.render(
34 |
35 |
36 |
, document.getElementById('root'))
37 | ```
38 |
39 | ## Guide
40 |
41 | The devtools will track any query you make through Horizon after the tools are
42 | initialized with `createDevTools`. If the query ends with `fetch()` the devtools
43 | will display the result of that query. If `watch()` is used 🔄 will
44 | show next to the query and be live updated with the results.
45 |
46 | `⌃ + Q` will show/hide the devtools.
47 |
48 | `⌃ + W` will change the position of the devtools.
49 |
50 | You can run custom queries using the query editor in the devtools. Type in a
51 | query into the bottom text box and press `⌃ + Enter` to run it. The horizon
52 | instance will be bound to `horizon`. If a query excludes `fetch()` or `watch()`,
53 | `fetch()` will be automatically appended. If a query excludes `subscribe()`,
54 | `subscribe()` will be automatically appended. For example, if `horizon('users')`
55 | is entered, `horizon('users').fetch().subscribe()` will be run.
56 |
57 | ## API
58 |
59 | ### `createDevTools(horizon): DevTools`
60 |
61 | Attaches instrumentation for monitoring on the horizon instance. It returns
62 | a `DevTools` component hooked up to the instrumentation that automatically
63 | updates.
64 |
65 | ### ``
66 |
67 | This renders the developer tools into the window. If `defaultVisible` is set
68 | to false, the tools will not be open at first. `defaultPosition` can be either
69 | `left`, `right`, `top`, or `bottom`.
70 |
71 | ## Contributing
72 |
73 | To run the example use `npm run dev` and go to `localhost:8181`.
74 |
75 | To build for publishing run `npm run build`.
76 |
--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rrdelaney/horizon-devtools/7aacba90a3c06a9e8840eabc8b6fa0ae9ac23a8e/assets/demo.gif
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "horizon-devtools",
3 | "version": "1.1.0",
4 | "description": "Devtools for horizon",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "concurrently -c green,blue --names \"Horizon,Webpack\" --prefix \"[{name}]\" \"hz serve --dev\" \"webpack --watch\"",
8 | "build": "babel src/devTools.jsx > index.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/rrdelaney/horizon-devtools"
13 | },
14 | "keywords": [
15 | "horizon",
16 | "react",
17 | "devtools",
18 | "hz"
19 | ],
20 | "author": "Ryan Delaney ",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/rrdelaney/horizon-devtools/issues"
24 | },
25 | "homepage": "https://github.com/rrdelaney/horizon-devtools",
26 | "devDependencies": {
27 | "babel-cli": "^6.11.4",
28 | "babel-preset-es2015": "^6.9.0",
29 | "babel-preset-react": "^6.11.1",
30 | "concurrently": "^2.2.0",
31 | "horizon": "^2.0.0",
32 | "react": "^15.2.1",
33 | "react-dom": "^15.2.1",
34 | "snazzy": "^4.0.0",
35 | "standard": "^7.1.2",
36 | "webpack": "^1.13.1"
37 | },
38 | "dependencies": {
39 | "react-dock": "^0.2.3",
40 | "react-json-tree": "^0.10.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/devTools.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import JSONTree from 'react-json-tree'
3 | import Dock from 'react-dock'
4 |
5 | // This should be replaced with Horizon 2's `query.toString()` method
6 | const ast2string = (ast, live) => {
7 | // All queries except `find` are an array, so we normalize `find` here to be an array
8 | if (ast.find) ast.find = [ast.find]
9 | return ['find', 'find_all', 'above', 'below', 'order', 'limit'].reduce(
10 | (res, key) => res + (!ast[key] ? '' : `.${key}(${JSON.stringify(ast[key][0]).replace(/[\[\]"\{\}]/g, '')})`),
11 | (live === 'watch' ? '🔄 ' : '') + ast.collection
12 | )
13 | }
14 |
15 | let positions = ['left', 'top', 'right', 'bottom']
16 |
17 | let nextPosition = c => positions[(positions.indexOf(c) + 1) % 4]
18 |
19 | const theme = {
20 | base00: '#1B2B34',
21 | base01: '#EC5F67',
22 | base03: '#99C794',
23 | base06: '#FAC863',
24 | base05: '#6699CC',
25 | base04: '#C594C5',
26 | base02: '#5FB3B3',
27 | base07: '#A7ADBA',
28 | base08: '#4F5B66',
29 | base09: '#EC5F67',
30 | base0A: '#99C794',
31 | base0B: '#FAC863',
32 | base0D: '#6699CC',
33 | base0C: '#C594C5',
34 | base0E: '#5FB3B3',
35 | base0F: '#D8DEE9'
36 | }
37 |
38 | function instrument (horizon) {
39 | let observ = {
40 | queries: {},
41 | update: () => null
42 | }
43 |
44 | Object.getPrototypeOf(Object.getPrototypeOf(horizon('users'))).fetch =
45 | new Proxy(Object.getPrototypeOf(Object.getPrototypeOf(horizon('users'))).fetch, {
46 | apply (_fetch, thisArg) {
47 | return _fetch.bind(thisArg)().map(c => {
48 | observ.queries[ast2string(thisArg._query, 'fetch')] = c
49 | observ.update()
50 | return c
51 | })
52 | }
53 | })
54 |
55 | Object.getPrototypeOf(Object.getPrototypeOf(horizon('users'))).watch =
56 | new Proxy(Object.getPrototypeOf(Object.getPrototypeOf(horizon('users'))).watch, {
57 | apply (_watch, thisArg, args) {
58 | return _watch.bind(thisArg)(...args).map(c => {
59 | observ.queries[ast2string(thisArg._query, 'watch')] = c
60 | observ.update()
61 | return c
62 | })
63 | }
64 | })
65 |
66 | return observ
67 | }
68 |
69 | const Button = ({ onClick, children }) =>
70 |
81 | {children}
82 |
83 |
84 | const ButtonTabs = ({ onClick }) =>
85 |