Automatically expand app-db path when adding it to browser

This commit is contained in:
Daniel Compton 2017-11-21 14:40:46 +13:00
parent 9395e12c48
commit 8b5f380232
3 changed files with 150 additions and 122 deletions

View File

@ -0,0 +1,145 @@
(ns day8.re-frame.trace.components.data-browser
(:require-macros [day8.re-frame.trace.utils.macros :refer [with-cljs-devtools-prefs]])
(:require [mranderson047.re-frame.v0v10v2.re-frame.core :as rf]
[day8.re-frame.trace.utils.localstorage :as localstorage]
[day8.re-frame.trace.components.components :as components]
[clojure.string :as str]
[reagent.core :as r]))
(defn string->css [css-string]
"This function converts jsonml css-strings to valid css maps for hiccup.
Example: 'margin-left:0px;min-height:14px;' converts to
{:margin-left '0px', :min-height '14px'}"
(->> (str/split css-string #";")
(map #(str/split % #":"))
(reduce (fn [acc [property value]]
(assoc acc (keyword property) value)) {})))
(declare jsonml->hiccup)
(def default-cljs-devtools-prefs @devtools.prefs/default-config)
(defn reset-wrapping [css-string]
(str/replace css-string #"white-space:nowrap;" ""))
(def customized-cljs-devtools-prefs
{; Override some cljs-devtools default styles.
; The goal here is to make default styles more flexible and wrap at the edge of our panel (we don't want horizontal
; scrolling). Technically we want to remove all 'white-space:no-wrap'.
; See https://github.com/binaryage/cljs-devtools/blob/master/src/lib/devtools/defaults.cljs
;; Commented out as this causes some other issues too.
;:header-style (reset-wrapping (:header-style default-cljs-devtools-prefs))
;:expandable-style (reset-wrapping (:expandable-style default-cljs-devtools-prefs))
;:item-style (reset-wrapping (:item-style default-cljs-devtools-prefs))
; Hide the index spans on the left hand of collections. Shows how many elements in a collection.
:none-style "display: none"
:index-tag [:span :none-style]
; Our JSON renderer does not have hierarchy depth limit,
; See https://github.com/binaryage/cljs-devtools/blob/master/src/lib/devtools/formatters/budgeting.cljs
:initial-hierarchy-depth-budget false})
(def effective-cljs-devtools-prefs (merge default-cljs-devtools-prefs customized-cljs-devtools-prefs))
(defn make-devtools-api-call [api-fn & args]
(with-cljs-devtools-prefs effective-cljs-devtools-prefs
(apply api-fn args)))
(defn cljs-devtools-header [& args]
(apply make-devtools-api-call devtools.formatters.core/header-api-call args))
(defn cljs-devtools-body [& args]
(apply make-devtools-api-call devtools.formatters.core/body-api-call args))
(defn cljs-devtools-has-body [& args]
(apply make-devtools-api-call devtools.formatters.core/has-body-api-call args))
(defn get-object [jsonml]
(.-object (get jsonml 1)))
(defn get-config [jsonml]
(.-config (get jsonml 1)))
(defn data-structure [jsonml path]
(let [expanded? (rf/subscribe [:app-db/node-expanded? path])]
(fn [jsonml path]
[:span
{:class (str "re-frame-trace--object" (when @expanded? " expanded"))}
[:span {:class "toggle"
:on-click #(rf/dispatch [:app-db/toggle-expansion path])}
[:button.expansion-button (if @expanded? "▼" "▶")]]
(if (and @expanded? (cljs-devtools-has-body (get-object jsonml) (get-config jsonml)))
(jsonml->hiccup
(cljs-devtools-body
(get-object jsonml)
(get-config jsonml))
(conj path :body))
(jsonml->hiccup
(cljs-devtools-header
(get-object jsonml)
(get-config jsonml))
(conj path :header)))])))
(defn jsonml->hiccup
"JSONML is the format used by Chrome's Custom Object Formatters.
The spec is at https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/preview.
JSONML is pretty much Hiccup over JSON. Chrome's implementation of this can
be found at https://cs.chromium.org/chromium/src/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js
"
[jsonml path]
(if (number? jsonml)
jsonml
(let [[tag-name attributes & children] jsonml
tagnames #{"div" "span" "ol" "li" "table" "tr" "td"}]
(cond
(contains? tagnames tag-name) (into
[(keyword tag-name) {:style (-> (js->clj attributes)
(get "style")
(string->css))}]
(map-indexed (fn [i child] (jsonml->hiccup child (conj path i))))
children)
(= tag-name "object") [data-structure jsonml path]
:else jsonml))))
(defn subtree [data title path]
(let [expanded? (rf/subscribe [:app-db/node-expanded? path])]
(fn [data]
[:div
{:class (str/join " " ["re-frame-trace--object"
(when @expanded? "expanded")])}
[:span {:class "toggle"
:on-click #(rf/dispatch [:app-db/toggle-expansion path])}
[:button.expansion-button (if @expanded? "▼ " "▶ ")]]
(or title "data")
[:div {:style {:margin-left 20}}
(cond
(and @expanded?
(or (string? data)
(number? data))) [:div {:style {:margin "10px 0"}} data]
@expanded? (jsonml->hiccup (cljs-devtools-header data) (conj path 0)))]])))
(defn subscription-render [data title path]
(let [expanded? (r/atom true) #_(rf/subscribe [:app-db/node-expanded? path])]
(fn [data]
[:div
{:class (str/join " " ["re-frame-trace--object"
(when @expanded? "expanded")])}
[:span {:class "toggle"
:on-click #(rf/dispatch [:app-db/toggle-expansion path])}
[:button.expansion-button (if @expanded? "▼ " "▶ ")]]
(or title "data")
[:div {:style {:margin-left 20}}
(cond
(and @expanded?
(or (string? data)
(number? data))) [:div {:style {:margin "10px 0"}} data]
@expanded? (jsonml->hiccup (cljs-devtools-header data) (conj path 0)))]])))

View File

@ -161,6 +161,7 @@
(fn [db [_ path]]
(let [new-db (update-in db [:app-db :paths] #(remove (fn [p] (= p path)) %))]
(localstorage/save! "app-db-paths" (get-in new-db [:app-db :paths]))
;; TODO: remove from json-ml expansions too.
new-db)))
(rf/reg-event-db
@ -174,6 +175,7 @@
nil))]
(if (some? path)
(do (localstorage/save! "app-db-paths" (cons path (get-in db [:app-db :paths])))
(rf/dispatch [:app-db/toggle-expansion [path]])
(-> db
(update-in [:app-db :paths] #(cons path %))
(assoc-in [:app-db :search-string] "")))

View File

@ -1,131 +1,12 @@
(ns day8.re-frame.trace.panels.app-db
(:require-macros [day8.re-frame.trace.utils.macros :refer [with-cljs-devtools-prefs]])
(:require [reagent.core :as r]
[clojure.string :as str]
[devtools.prefs]
[devtools.formatters.core]
[day8.re-frame.trace.utils.localstorage :as localstorage]
[day8.re-frame.trace.components.components :as components]
[day8.re-frame.trace.components.data-browser :as data-browser]
[day8.re-frame.trace.utils.re-com :as re-com]
[mranderson047.re-frame.v0v10v2.re-frame.core :as rf]))
(defn string->css [css-string]
"This function converts jsonml css-strings to valid css maps for hiccup.
Example: 'margin-left:0px;min-height:14px;' converts to
{:margin-left '0px', :min-height '14px'}"
(->> (str/split css-string #";")
(map #(str/split % #":"))
(reduce (fn [acc [property value]]
(assoc acc (keyword property) value)) {})))
(declare jsonml->hiccup)
(def default-cljs-devtools-prefs @devtools.prefs/default-config)
(defn reset-wrapping [css-string]
(str/replace css-string #"white-space:nowrap;" ""))
(def customized-cljs-devtools-prefs
{; Override some cljs-devtools default styles.
; The goal here is to make default styles more flexible and wrap at the edge of our panel (we don't want horizontal
; scrolling). Technically we want to remove all 'white-space:no-wrap'.
; See https://github.com/binaryage/cljs-devtools/blob/master/src/lib/devtools/defaults.cljs
;; Commented out as this causes some other issues too.
;:header-style (reset-wrapping (:header-style default-cljs-devtools-prefs))
;:expandable-style (reset-wrapping (:expandable-style default-cljs-devtools-prefs))
;:item-style (reset-wrapping (:item-style default-cljs-devtools-prefs))
; Hide the index spans on the left hand of collections. Shows how many elements in a collection.
:none-style "display: none"
:index-tag [:span :none-style]
; Our JSON renderer does not have hierarchy depth limit,
; See https://github.com/binaryage/cljs-devtools/blob/master/src/lib/devtools/formatters/budgeting.cljs
:initial-hierarchy-depth-budget false})
(def effective-cljs-devtools-prefs (merge default-cljs-devtools-prefs customized-cljs-devtools-prefs))
(defn make-devtools-api-call [api-fn & args]
(with-cljs-devtools-prefs effective-cljs-devtools-prefs
(apply api-fn args)))
(defn cljs-devtools-header [& args]
(apply make-devtools-api-call devtools.formatters.core/header-api-call args))
(defn cljs-devtools-body [& args]
(apply make-devtools-api-call devtools.formatters.core/body-api-call args))
(defn cljs-devtools-has-body [& args]
(apply make-devtools-api-call devtools.formatters.core/has-body-api-call args))
(defn get-object [jsonml]
(.-object (get jsonml 1)))
(defn get-config [jsonml]
(.-config (get jsonml 1)))
(defn data-structure [jsonml path]
(let [expanded? (rf/subscribe [:app-db/node-expanded? path])]
(fn [jsonml path]
[:span
{:class (str "re-frame-trace--object" (when @expanded? " expanded"))}
[:span {:class "toggle"
:on-click #(rf/dispatch [:app-db/toggle-expansion path])}
[:button.expansion-button (if @expanded? "▼" "▶")]]
(if (and @expanded? (cljs-devtools-has-body (get-object jsonml) (get-config jsonml)))
(jsonml->hiccup
(cljs-devtools-body
(get-object jsonml)
(get-config jsonml))
(conj path :body))
(jsonml->hiccup
(cljs-devtools-header
(get-object jsonml)
(get-config jsonml))
(conj path :header)))])))
(defn jsonml->hiccup
"JSONML is the format used by Chrome's Custom Object Formatters.
The spec is at https://docs.google.com/document/d/1FTascZXT9cxfetuPRT2eXPQKXui4nWFivUnS_335T3U/preview.
JSONML is pretty much Hiccup over JSON. Chrome's implementation of this can
be found at https://cs.chromium.org/chromium/src/third_party/WebKit/Source/devtools/front_end/object_ui/CustomPreviewComponent.js
"
[jsonml path]
(if (number? jsonml)
jsonml
(let [[tag-name attributes & children] jsonml
tagnames #{"div" "span" "ol" "li" "table" "tr" "td"}]
(cond
(contains? tagnames tag-name) (into
[(keyword tag-name) {:style (-> (js->clj attributes)
(get "style")
(string->css))}]
(map-indexed (fn [i child] (jsonml->hiccup child (conj path i))))
children)
(= tag-name "object") [data-structure jsonml path]
:else jsonml))))
(defn subtree [data title path]
(let [expanded? (rf/subscribe [:app-db/node-expanded? path])]
(fn [data]
[:div
{:class (str/join " " ["re-frame-trace--object"
(when @expanded? "expanded")])}
[:span {:class "toggle"
:on-click #(rf/dispatch [:app-db/toggle-expansion path])}
[:button.expansion-button (if @expanded? "▼ " "▶ ")]]
(or title "data")
[:div {:style {:margin-left 20}}
(cond
(and @expanded?
(or (string? data)
(number? data))) [:div {:style {:margin "10px 0"}} data]
@expanded? (jsonml->hiccup (cljs-devtools-header data) (conj path 0)))]])))
(defn render-state [data]
(let [subtree-input (r/atom "")
subtree-paths (rf/subscribe [:app-db/paths])
@ -152,7 +33,7 @@
^{:key path}
[:div.subtree-wrapper {:style {:margin "10px 0"}}
[:div.subtree
[subtree
[data-browser/subtree
(get-in @data path)
[:button.subtree-button {:on-click #(rf/dispatch [:app-db/remove-path path])}
[:span.subtree-button-string
@ -160,4 +41,4 @@
[path]]]])
@subtree-paths))]
[:div {:style {:margin-bottom "20px"}}
[subtree @data [:span.label "app-db"] [:app-db]]]]])))
[data-browser/subtree @data [:span.label "app-db"] [:app-db]]]]])))