(ns weathergen.ui.trees (:require [cljs.pprint :refer [pprint]] [weathergen.ui.common :as comm :refer [inl px pct styled]] [rum.core :as rum] [taoensso.timbre :as log :refer-macros ^{:line 9, :column 28} (log trace debug info warn error fatal report logf tracef debugf infof warnf errorf fatalf reportf spy get-env log-env)] [javelin.core :refer [->Cell input? cell cell? destroy-cell! lift lens? set-formula! constant? cell-doseq* deref* set-cell! lens formula? alts! dosync* cell-map formula]] [hoplon.jquery :refer [set-attributes! check-val! set-styles! text-val!]] [hoplon.core :refer [form audio input menuitem hgroup do! timeout $text base h1 set-attributes! embed shadow h3 body keygen progress main cite on-page-load i p nav ruby a menu blockquote img $comment span track data u dl select html thead del fieldset aside figure figcaption q on! bdi append-child! video address caption dd rp hr tbody table html-var add-initfn! pre ul replace-child! html-time html-map sup dfn sub mark script button wbr insert-before! strong normalize-class li dt rtc td tr section th optgroup bust-cache iframe remove-child! legend em kbd spliced html-object article abbr template multicol prerendering? <!-- source output set-styles! route-cell header datalist tfoot s ins footer title h5 canvas param div option summary samp small style textarea loop-tpl* h4 head ol details col label picture rt when-dom h6 link page-load colgroup meter html-meta static-elements bdo --> b attribute? code dialog noframes do-watch noscript safe-nth h2 area br]]) (:require-macros [weathergen.cljs.macros :refer [formula-of]] [javelin.core :refer [with-let mx2 dosync cell= set-cell!= prop-cell cell-doseq formulet defc defc= macroexpand-all mx formula-of cell-let]] [hoplon.core :refer [definterval text elem+ cache-key with-timeout defelem+ when-tpl static sexp defelem elem def-values if-tpl cond-tpl with-page-load for-tpl with-dom case-tpl loop-tpl with-interval with-init!]]))













;; (set! *warn-on-infer* true)

(defn- tree*
  "Helper function for the tree control."
  [expand-state levels items level-number]
  (let [[level & more-levels] levels
        {:keys [formatter children attrs]} level]
    (styled
     :garden [:li {:margin-left "-28px"}]
     (ol
      :css {:list-style-type "none"}
      (for-tpl [item items]
        (let [kids (if children (children item) (cell nil))
              expanded? (cell true)]
          ;; This looks like it might be a real problem. When the list
          ;; contracts, the do-watch is still active on the now
          ;; non-existent expanded? cell. Or something like that.
          ;; Basically: I hate the loop template macros.
          (when expand-state
            (do-watch expand-state
                      (fn [_ n]
                        (cond
                          (= n :all-expanded)
                          (reset! expanded? true)

                          (= n :all-collapsed)
                          (reset! expanded? false)

                          (= (first n) :expand-through-level)
                          (let [e (< level-number (second n))]
                            (reset! expanded? e))))))
          (li
           (if attrs
             (attrs item)
             {})
           (inl
            :css {:white-space "nowrap"}
            (inl :css {:width "15px"
                       :font-size "120%"
                       :vertical-align "top"}
                 (if-tpl (-> kids seq cell=)
                   (div :css {:display "inline-block"
                              :cursor "pointer"
                              :position "relative"
                              :background "transparent"}
                        :click (fn []
                                 (dosync
                                  (swap! expanded? not)
                                  (when expand-state (reset! expand-state nil))))
                        (div
                         :css (formula-of [expanded?]
                                {:display "inline-block"
                                 :width 0
                                 :height 0
                                 :border-left "5px solid transparent"
                                 :border-right "5px solid transparent"
                                 :border-top "7px solid black"
                                 :vertical-align "middle"
                                 :transform (str "rotate("
                                                 (if expanded?
                                                   0
                                                   -90)
                                                 "deg)")})))
                   ""))
            (formatter expanded? item))
           (when-tpl (cell= (and expanded? kids))
             (tree* expand-state more-levels kids (inc level-number))))))))))

(defn tree
  "Returns a collapable tree with hierarchy defined by `levels`, a seq
  of maps with `:formatter` and `:children` keys, each a function of a
  single item in the hierarchy. The formatter returns UI for that
  item, and children (if present) returns items that will be rendered
  as children. `expand-state` is either nil or a cell containing
  keyword `:all-expanded`, keyword `:all-collapsed`, or vector
  `[:expand-through n]`, where `n` is a nonnegative integer. Changing
  to these values will cause the list to behave as specified."
  [expand-state levels items]
  (tree* expand-state levels items 0))

(defn inline
  [props & args]
  [:div
   (assoc-in props [:style :display] "inline-block")
   args])

(rum/defc RTree
  [expander items]
  [:ol
   {:style {:list-style-type "none"}}
   (for [{:keys [ui attrs expanded? children id] :as item} items]
     [:li
      (assoc attrs :key id)
      [:div
       {:style {:display     "inline-flex"
                :white-space "nowrap"}}
       (inline
        {:style {:width          (px 15)
                 :font-size      (pct 120)
                 :vertical-align "top"}}
        (if (empty? children)
          ""
          (inline {:style    {:cursor     "pointer"
                              :position   "relative"
                              :background "transparent"}
                   :on-click (fn [e]
                               (expander id))}
                  (inline
                   {:style {:width          0
                            :height         0
                            :border-left    "5px solid transparent"
                            :border-right   "5px solid transparent"
                            :border-top     "7px solid black"
                            :vertical-align "middle"
                            :margin-top     (px -8)
                            :transform      (str "rotate("
                                                 (if expanded?
                                                   0
                                                   -90)
                                                 "deg)")}}))))
       ui]
      (when (and expanded? children)
        (RTree expander children))])])

(defn rtree
  [items expander]
  (with-let [parent (styled
                     :garden [[:ol {:text-indent (px 2)}]
                              [:li {:margin-left (px -28)}]])]
    (do-watch items
              (fn [_ items*]
                (rum/mount (RTree expander items*) parent)))))

(comment
  [{:ui ...
    :expanded? true
    :children [{:ui ...
                :expanded? false}
               {:ui ...
                :expanded? true}]}])
