Merge branch 'master' into feature/extend-filter
This commit is contained in:
commit
654925545e
23
README.md
23
README.md
|
@ -99,3 +99,26 @@ You need both the re-frame-trace project _and_ a test project to develop it agai
|
|||
```
|
||||
|
||||
- Now run your test project however you usually run it, and re-frame-trace should be in there. \o/
|
||||
|
||||
- Additionally, if modifying the `.less` CSS files, compile the css by running within the re-frame-trace directory:
|
||||
|
||||
```
|
||||
lein less auto
|
||||
```
|
||||
|
||||
to watch for changes, or one time by running:
|
||||
|
||||
```
|
||||
lein less once
|
||||
```
|
||||
|
||||
|
||||
### Developing CSS
|
||||
|
||||
The CSS for the trace panel are defined both inline and within `src/day8/re_frame/less`. To develop the styles, run
|
||||
|
||||
```
|
||||
lein less auto
|
||||
```
|
||||
|
||||
and the .less file will automatically compile to css on file changes. Don't edit the file within `src/day8/re_frame/css` directly, or it will be overwriten. We are using css preprocessing because in order to isolate the panel styles, we are namespacing the panel styles with the id `#--re-frame-trace--`.
|
||||
|
|
19
project.clj
19
project.clj
|
@ -1,4 +1,4 @@
|
|||
(defproject day8.re-frame/trace "0.1.4-SNAPSHOT"
|
||||
(defproject day8.re-frame/trace "0.1.6-SNAPSHOT"
|
||||
:description "Tracing and developer tools for re-frame apps"
|
||||
:url "https://github.com/Day8/re-frame-trace"
|
||||
:license {:name "MIT"}
|
||||
|
@ -7,6 +7,23 @@
|
|||
[reagent "0.6.0"]
|
||||
[re-frame "0.9.0"]
|
||||
[cljsjs/d3 "4.2.2-0"]]
|
||||
:plugins [[lein-less "1.7.5"]]
|
||||
:deploy-repositories {"releases" :clojars
|
||||
"snapshots" :clojars}
|
||||
|
||||
:release-tasks [["vcs" "assert-committed"]
|
||||
["change" "version" "leiningen.release/bump-version" "release"]
|
||||
["less" "once"]
|
||||
["vcs" "commit"]
|
||||
["vcs" "tag"]
|
||||
["deploy"]
|
||||
["change" "version" "leiningen.release/bump-version"]
|
||||
["vcs" "commit"]
|
||||
["vcs" "push"]]
|
||||
|
||||
:figwheel {:css-dirs ["resources/day8/re_frame/trace"]}
|
||||
|
||||
:less {:source-paths ["resources/day8/re_frame/trace"]
|
||||
:target-path "resources/day8/re_frame/trace"}
|
||||
|
||||
:profiles {:dev {:dependencies [[binaryage/dirac "RELEASE"]]}})
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#--re-frame-trace-- {
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: 'courier new', monospace;
|
||||
}
|
||||
#--re-frame-trace-- tbody {
|
||||
color: #aaa;
|
||||
}
|
||||
#--re-frame-trace-- tr:hover {
|
||||
transition: all 0.1s ease-out;
|
||||
background: aliceblue;
|
||||
filter: brightness(90%);
|
||||
}
|
||||
#--re-frame-trace-- tr:nth-child(even) {
|
||||
background: aliceblue;
|
||||
}
|
||||
#--re-frame-trace-- .button {
|
||||
padding: 5px 5px 3px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#--re-frame-trace-- .text-button {
|
||||
border-bottom: 1px dotted #888;
|
||||
font-weight: normal;
|
||||
}
|
||||
#--re-frame-trace-- .button:focus,
|
||||
#--re-frame-trace-- .text-button:focus {
|
||||
border-radius: 2px 2px 0 0;
|
||||
-webkit-box-shadow: inset 0px -5px 0px 0px rgba(0, 0, 0, 0.3);
|
||||
-moz-box-shadow: inset 0px -5px 0px 0px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: inset 0px -5px 0px 0px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
#--re-frame-trace-- .icon-button {
|
||||
font-size: 10px;
|
||||
}
|
||||
#--re-frame-trace-- .tab {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
text-transform: uppercase;
|
||||
font-family: monospace;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 4px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
#--re-frame-trace-- .tab.active {
|
||||
background: transparent;
|
||||
border-bottom: 3px solid lightblue;
|
||||
border-radius: 0;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
#--re-frame-trace-- ul.filter-items {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 -5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#--re-frame-trace-- .filter-items li {
|
||||
color: #333;
|
||||
background: #efefef;
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
margin: 5px;
|
||||
}
|
||||
#--re-frame-trace-- .filter-items li .filter-item-string {
|
||||
color: #616cdb;
|
||||
}
|
||||
#--re-frame-trace-- .icon {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke-width: 0;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
#--re-frame-trace-- .icon-remove {
|
||||
margin-left: 10px;
|
||||
}
|
||||
#--re-frame-trace-- select {
|
||||
background: white;
|
||||
font-family: 'courier new', monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
#--re-frame-trace-- .nav {
|
||||
background: #efeef1;
|
||||
color: #222;
|
||||
}
|
||||
#--re-frame-trace-- .panel-content-top {
|
||||
flex: 1;
|
||||
}
|
||||
#--re-frame-trace-- .panel-content-scrollable {
|
||||
margin: 10px 0 0 10px;
|
||||
flex: 1 0 auto;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
#--re-frame-trace-- .filter-control {
|
||||
margin: 10px 0 0 10px;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
#--re-frame-trace-- {
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: 'courier new', monospace;
|
||||
|
||||
tbody {
|
||||
color: #aaa;
|
||||
}
|
||||
tr:hover {
|
||||
transition: all 0.1s ease-out;
|
||||
background: aliceblue;
|
||||
filter: brightness(90%);
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background: aliceblue;
|
||||
}
|
||||
.button {
|
||||
padding: 5px 5px 3px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.text-button {
|
||||
border-bottom: 1px dotted #888;
|
||||
font-weight: normal;
|
||||
}
|
||||
.button:focus, .text-button:focus {
|
||||
border-radius: 2px 2px 0 0;
|
||||
-webkit-box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
-moz-box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
}
|
||||
.icon-button {
|
||||
font-size: 10px;
|
||||
}
|
||||
button.tab {
|
||||
|
||||
}
|
||||
.tab {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
text-transform: uppercase;
|
||||
font-family: monospace;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 4px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.tab.active {
|
||||
background: transparent;
|
||||
border-bottom: 3px solid lightblue;
|
||||
border-radius: 0;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
ul.filter-items {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 -5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.filter-items li {
|
||||
color: #333;
|
||||
background: #efefef;
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
margin: 5px;
|
||||
}
|
||||
.filter-items {
|
||||
li {
|
||||
.filter-item-string {
|
||||
color: #616cdb;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.icon {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke-width: 0;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
.icon-remove {
|
||||
margin-left: 10px;
|
||||
}
|
||||
select {
|
||||
background: white;
|
||||
font-family: 'courier new', monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
.nav {
|
||||
background: #efeef1;
|
||||
color: #222;
|
||||
}
|
||||
.panel-content-top {
|
||||
flex: 1;
|
||||
}
|
||||
.panel-content-scrollable {
|
||||
margin: 10px 0 0 10px;
|
||||
flex: 1 0 auto;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.filter-control {
|
||||
margin: 10px 0 0 10px;
|
||||
}
|
||||
}
|
|
@ -95,6 +95,7 @@
|
|||
|
||||
|
||||
(def traces (interop/ratom []))
|
||||
|
||||
(defn log-trace? [trace]
|
||||
(let [rendering? (= (:op-type trace) :render)]
|
||||
(if-not rendering?
|
||||
|
@ -252,29 +253,30 @@
|
|||
(:filter-type item) ": " [:span.filter-item-string (:query item)]
|
||||
[:span.icon-button [components/icon-remove]]]])
|
||||
@filter-items)]]
|
||||
[:div.panel-content-scrollable
|
||||
[:table
|
||||
{:cell-spacing "0" :width "100%"}
|
||||
[:thead>tr
|
||||
[:th [:button.text-button
|
||||
{:style {:cursor "pointer"}
|
||||
:on-click (fn [ev]
|
||||
;; Always reset expansions
|
||||
(swap! trace-detail-expansions assoc :overrides {})
|
||||
;; Then toggle :show-all?
|
||||
(swap! trace-detail-expansions update :show-all? not))}
|
||||
(if (:show-all? @trace-detail-expansions) "-" "+")]]
|
||||
[:th "operations"]
|
||||
[:th
|
||||
(when (pos? (count @filter-items))
|
||||
(str (count showing-traces) " of "))
|
||||
(when (pos? (count @traces))
|
||||
(str (count @traces)))
|
||||
" events "
|
||||
(when (pos? (count @traces))
|
||||
[:span "(" [:button.text-button {:on-click #(do (trace/reset-tracing!) (reset! traces []))} "clear"] ")"])]
|
||||
[:th "meta"]]
|
||||
[:tbody (render-traces showing-traces filter-items filter-input trace-detail-expansions)]]]]))))
|
||||
[components/autoscroll-list {:class "panel-content-scrollable" :scroll? true}
|
||||
[:table
|
||||
{:style {:margin-bottom 10}
|
||||
:cell-spacing "0" :width "100%"}
|
||||
[:thead>tr
|
||||
[:th [:button.text-button
|
||||
{:style {:cursor "pointer"}
|
||||
:on-click (fn [ev]
|
||||
;; Always reset expansions
|
||||
(swap! trace-detail-expansions assoc :overrides {})
|
||||
;; Then toggle :show-all?
|
||||
(swap! trace-detail-expansions update :show-all? not))}
|
||||
(if (:show-all? @trace-detail-expansions) "-" "+")]]
|
||||
[:th "operations"]
|
||||
[:th
|
||||
(when (pos? (count @filter-items))
|
||||
(str (count showing-traces) " of "))
|
||||
(when (pos? (count @traces))
|
||||
(str (count @traces)))
|
||||
" events "
|
||||
(when (pos? (count @traces))
|
||||
[:span "(" [:button.text-button {:on-click #(do (trace/reset-tracing!) (reset! traces []))} "clear"] ")"])]
|
||||
[:th "meta"]]
|
||||
[:tbody (render-traces showing-traces filter-items filter-input trace-detail-expansions)]]]]))))
|
||||
|
||||
(defn resizer-style [draggable-area]
|
||||
{:position "absolute" :z-index 2 :opacity 0
|
||||
|
@ -282,12 +284,17 @@
|
|||
|
||||
(def ease-transition "left 0.2s ease-out, top 0.2s ease-out, width 0.2s ease-out, height 0.2s ease-out")
|
||||
|
||||
(defn toggle-traces [showing?]
|
||||
(if @showing?
|
||||
(enable-tracing!)
|
||||
(disable-tracing!)))
|
||||
|
||||
(defn devtools []
|
||||
;; Add clear button
|
||||
;; Filter out different trace types
|
||||
(let [position (r/atom :right)
|
||||
panel-width-ratio (r/atom (localstorage/get "panel-width-ratio" 0.35))
|
||||
showing? (r/atom false)
|
||||
showing? (r/atom (localstorage/get "show-panel" false))
|
||||
dragging? (r/atom false)
|
||||
pin-to-bottom? (r/atom true)
|
||||
selected-tab (r/atom :traces)
|
||||
|
@ -301,9 +308,7 @@
|
|||
(cond
|
||||
(and (= key "h") (.-ctrlKey e))
|
||||
(do (swap! showing? not)
|
||||
(if @showing?
|
||||
(enable-tracing!)
|
||||
(disable-tracing!))
|
||||
(toggle-traces showing?)
|
||||
(.preventDefault e))))))
|
||||
handle-mousemove (fn [e]
|
||||
(when @dragging?
|
||||
|
@ -311,13 +316,17 @@
|
|||
y (.-clientY e)]
|
||||
(.preventDefault e)
|
||||
(reset! panel-width-ratio (/ (- window-width x) window-width)))))]
|
||||
|
||||
(add-watch panel-width-ratio
|
||||
:update-panel-width-ratio
|
||||
(fn [_ _ _ new-state]
|
||||
(localstorage/save! "panel-width-ratio" new-state)))
|
||||
(add-watch showing?
|
||||
:update-show-panel
|
||||
(fn [_ _ _ new-state]
|
||||
(localstorage/save! "show-panel" new-state)))
|
||||
(r/create-class
|
||||
{:component-will-mount (fn []
|
||||
(toggle-traces showing?)
|
||||
(js/window.addEventListener "keydown" handle-keys)
|
||||
(js/window.addEventListener "mousemove" handle-mousemove))
|
||||
:component-will-unmount (fn []
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
(ns day8.re-frame.trace.components)
|
||||
(ns day8.re-frame.trace.components
|
||||
(:require [reagent.core :as r]
|
||||
[goog.fx.dom :as fx]))
|
||||
|
||||
(defn icon-add []
|
||||
[:svg.icon.icon-add
|
||||
|
@ -13,3 +15,36 @@
|
|||
[:title "remove"]
|
||||
[:path
|
||||
{:d "M31.708 25.708c-0-0-0-0-0-0l-9.708-9.708 9.708-9.708c0-0 0-0 0-0 0.105-0.105 0.18-0.227 0.229-0.357 0.133-0.356 0.057-0.771-0.229-1.057l-4.586-4.586c-0.286-0.286-0.702-0.361-1.057-0.229-0.13 0.048-0.252 0.124-0.357 0.228 0 0-0 0-0 0l-9.708 9.708-9.708-9.708c-0-0-0-0-0-0-0.105-0.104-0.227-0.18-0.357-0.228-0.356-0.133-0.771-0.057-1.057 0.229l-4.586 4.586c-0.286 0.286-0.361 0.702-0.229 1.057 0.049 0.13 0.124 0.252 0.229 0.357 0 0 0 0 0 0l9.708 9.708-9.708 9.708c-0 0-0 0-0 0-0.104 0.105-0.18 0.227-0.229 0.357-0.133 0.355-0.057 0.771 0.229 1.057l4.586 4.586c0.286 0.286 0.702 0.361 1.057 0.229 0.13-0.049 0.252-0.124 0.357-0.229 0-0 0-0 0-0l9.708-9.708 9.708 9.708c0 0 0 0 0 0 0.105 0.105 0.227 0.18 0.357 0.229 0.356 0.133 0.771 0.057 1.057-0.229l4.586-4.586c0.286-0.286 0.362-0.702 0.229-1.057-0.049-0.13-0.124-0.252-0.229-0.357z"}]])
|
||||
|
||||
(defn scroll! [el start end time]
|
||||
(.play (fx/Scroll. el (clj->js start) (clj->js end) time)))
|
||||
|
||||
(defn scrolled-to-end? [el tolerance]
|
||||
;; at-end?: element.scrollHeight - element.scrollTop === element.clientHeight
|
||||
(> tolerance (- (.-scrollHeight el) (.-scrollTop el) (.-clientHeight el))))
|
||||
|
||||
(defn autoscroll-list [{:keys [class scroll?]} child]
|
||||
"Reagent component that enables scrolling for the elements of its child dom-node.
|
||||
Scrolling is only enabled if the list is scrolled to the end.
|
||||
Scrolling can be set as option for debugging purposes.
|
||||
Thanks to Martin Klepsch! Original code can be found here:
|
||||
https://gist.github.com/martinklepsch/440e6fd96714fac8c66d892e0be2aaa0"
|
||||
(let [node (r/atom nil)
|
||||
should-scroll (r/atom true)]
|
||||
(r/create-class
|
||||
{:display-name "autoscroll-list"
|
||||
:component-did-mount
|
||||
(fn [_]
|
||||
(scroll! @node [0 (.-scrollTop @node)] [0 (.-scrollHeight @node)] 0))
|
||||
:component-will-update
|
||||
(fn [_]
|
||||
(reset! should-scroll (scrolled-to-end? @node 100)))
|
||||
:component-did-update
|
||||
(fn [_]
|
||||
(when (and scroll? @should-scroll)
|
||||
(scroll! @node [0 (.-scrollTop @node)] [0 (.-scrollHeight @node)] 1600)))
|
||||
:reagent-render
|
||||
(fn [{:keys [class]} child]
|
||||
[:div {:class class :ref (fn [dom-node]
|
||||
(reset! node dom-node))}
|
||||
child])})))
|
|
@ -0,0 +1,7 @@
|
|||
(ns day8.re-frame.trace.macros
|
||||
(:require [clojure.java.io :as io]))
|
||||
|
||||
(defmacro slurp-macro
|
||||
"Reads a file as a string. Slurp is wrapped in a macro so it can interact with local files before clojurescript compilation."
|
||||
[path]
|
||||
(slurp (io/resource path)))
|
|
@ -1,111 +1,4 @@
|
|||
(ns day8.re-frame.trace.styles)
|
||||
(ns day8.re-frame.trace.styles
|
||||
(:require-macros [day8.re-frame.trace.macros :as macros]))
|
||||
|
||||
(defonce panel-styles "
|
||||
#--re-frame-trace-- {
|
||||
background: white;
|
||||
color: black;
|
||||
font-family: 'courier new', monospace;
|
||||
}
|
||||
#--re-frame-trace-- tbody {
|
||||
color: #aaa;
|
||||
}
|
||||
#--re-frame-trace-- tr:hover {
|
||||
transition: all 0.1s ease-out;
|
||||
background: aliceblue;
|
||||
filter: brightness(90%);
|
||||
}
|
||||
#--re-frame-trace-- tr:nth-child(even) {
|
||||
background: aliceblue;
|
||||
}
|
||||
#--re-frame-trace-- .button {
|
||||
padding: 5px 5px 3px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#--re-frame-trace-- .text-button {
|
||||
border-bottom: 1px dotted #888;
|
||||
font-weight: normal;
|
||||
}
|
||||
#--re-frame-trace-- .button:focus, .text-button:focus {
|
||||
border-radius: 2px 2px 0 0;
|
||||
-webkit-box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
-moz-box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
box-shadow: inset 0px -5px 0px 0px rgba(0,0,0,0.3);
|
||||
}
|
||||
#--re-frame-trace-- .icon-button {
|
||||
font-size: 10px;
|
||||
}
|
||||
#--re-frame-trace-- button.tab {
|
||||
|
||||
}
|
||||
#--re-frame-trace-- .tab {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
text-transform: uppercase;
|
||||
font-family: monospace;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 4px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
#--re-frame-trace-- .tab.active {
|
||||
background: transparent;
|
||||
border-bottom: 3px solid lightblue;
|
||||
border-radius: 0;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
#--re-frame-trace-- ul.filter-items {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0 -5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
#--re-frame-trace-- .filter-items li {
|
||||
color: #333;
|
||||
background: #efefef;
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
margin: 5px;
|
||||
}
|
||||
#--re-frame-trace-- .filter-items li .filter-item-string {
|
||||
color: #616cdb;
|
||||
}
|
||||
#--re-frame-trace-- .op-string:hover {
|
||||
cursor: pointer;
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
width: fit-content;
|
||||
}
|
||||
#--re-frame-trace-- .icon {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
stroke-width: 0;
|
||||
stroke: currentColor;
|
||||
fill: currentColor;
|
||||
}
|
||||
#--re-frame-trace-- .icon-remove {
|
||||
margin-left: 10px;
|
||||
}
|
||||
#--re-frame-trace-- select {
|
||||
background: white;
|
||||
font-family: 'courier new', monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
#--re-frame-trace-- .nav {
|
||||
background: #efeef1;
|
||||
color: #222;
|
||||
}
|
||||
#--re-frame-trace-- .panel-content-top {
|
||||
flex: 1;
|
||||
}
|
||||
#--re-frame-trace-- .panel-content-scrollable {
|
||||
margin: 10px 0 0 10px;
|
||||
flex: 1 0 auto;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
#--re-frame-trace-- .filter-control {
|
||||
margin: 10px 0 0 10px;
|
||||
}
|
||||
")
|
||||
(def panel-styles (macros/slurp-macro "day8/re_frame/trace/main.css"))
|
||||
|
|
Loading…
Reference in New Issue