|
@@ -5,18 +5,21 @@
|
|
|
|
|
|
; to generate random values
|
|
|
;for(let i = 0, s = new Set(); i < 10; i++){ let r = Math.floor(Math.random() * 15)+1, c = a[Math.floor(Math.random() * a.length)], k = `${c}${r}`; if(s.has(k)){ i--; continue; } s.add(k); v.push(`{:row ${r} :col "${c}" :value "${Math.floor(Math.random() * 10000)}"}`); }
|
|
|
-(def sample-data [{:row 1 :col "A" :value "59"}
|
|
|
- {:row 5 :col "C" :value "269"}
|
|
|
- {:row 4 :col "B" :value "7893"}
|
|
|
- {:row 2 :col "F" :value "8650"}
|
|
|
- {:row 6 :col "D" :value "4065"}
|
|
|
- {:row 7 :col "F" :value "5316"}
|
|
|
- {:row 12 :col "A" :value "2405"}
|
|
|
- {:row 5 :col "B" :value "7863"}
|
|
|
- {:row 9 :col "E" :value "3144"}
|
|
|
- {:row 10 :col "D" :value "8272"}
|
|
|
- {:row 11 :col "D" :value "2495"}
|
|
|
- {:row 15 :col "E" :value "8968"}])
|
|
|
+(def sample-data [{:row 1 :col "A" :value "59" :view :display}
|
|
|
+ {:row 5 :col "C" :value "269" :view :display}
|
|
|
+ {:row 4 :col "B" :value "7893" :view :display}
|
|
|
+ {:row 2 :col "F" :value "8650" :view :display}
|
|
|
+ {:row 6 :col "D" :value "4065" :view :display}
|
|
|
+ {:row 7 :col "F" :value "5316" :view :display}
|
|
|
+ {:row 12 :col "A" :value "2405" :view :display}
|
|
|
+ {:row 5 :col "B" :value "7863" :view :display}
|
|
|
+ {:row 9 :col "E" :value "3144" :view :display}
|
|
|
+ {:row 10 :col "D" :value "8272" :view :display}
|
|
|
+ {:row 11 :col "D" :value "2495" :view :display}
|
|
|
+ {:row 15 :col "E" :value "8968" :view :display}
|
|
|
+ {:row 7 :col "B" :value "=C5 + D6" :view :display}
|
|
|
+ {:row 8 :col "B" :value "=B7 * 2" :view :display}
|
|
|
+ {:row 7 :col "C" :value "=D1" :view :display}])
|
|
|
|
|
|
(defonce data-atom (r/atom sample-data))
|
|
|
|
|
@@ -39,38 +42,14 @@
|
|
|
(if (nil? datum)
|
|
|
(swap! data-atom conj {:row r :col c :value value})
|
|
|
(swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :value value) %) d)))))
|
|
|
+(defn update-display [c r display]
|
|
|
+ (println (str "trying to update " c r " to " display))
|
|
|
+ (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :display display) %) d))))
|
|
|
+(defn toggle-display [c r view-mode]
|
|
|
+ (println (str "toggling " c r " to " view-mode))
|
|
|
+ (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :view view-mode) %) d))))
|
|
|
|
|
|
-
|
|
|
-;; -------------------------
|
|
|
-;; Views
|
|
|
-
|
|
|
-(defn cell [c r data]
|
|
|
- (let [datum (some #(if (and (= c (:col %)) (= r (:row %))) %) data)]
|
|
|
- ^{:key (str c r)} [:td [:input {:value (or (:value datum) "")
|
|
|
- :on-change #(update-value c r datum (.. % -target -value))
|
|
|
- :on-blur (fn [e] (println (str "blur! " c r)))
|
|
|
- :on-focus (fn [e] (println (str "focus! " c r)))}]]
|
|
|
- )
|
|
|
- )
|
|
|
-(defn row [r cols data]
|
|
|
- ^{:key (str "row-" r)} [:tr
|
|
|
- (cons
|
|
|
- ^{:key (str "row-head-" r)} [:th (str r)]
|
|
|
- (map #(cell % r data) cols))])
|
|
|
-(defn header-row [cols]
|
|
|
- ^{:key "header"} [:tr
|
|
|
- (cons
|
|
|
- ^{:key "corner"} [:th]
|
|
|
- (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))])
|
|
|
-
|
|
|
-(defn sheet [data]
|
|
|
- [:table [:tbody
|
|
|
- (let [maxrow (highest :row data)
|
|
|
- cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
|
|
|
- (cons
|
|
|
- (header-row cols)
|
|
|
- (map #(row % cols data) (range 1 (inc maxrow)))))
|
|
|
- ]])
|
|
|
+; CALCULATION / FORMULA EVALUATION FUNCTIONS
|
|
|
|
|
|
(defn temp1 [data]
|
|
|
(let [first-order (group-by #(not= (first (:value %)) "=") data)
|
|
@@ -81,11 +60,22 @@
|
|
|
{:remaining (zipmap non-evaluated-cells non-evaluated-values)
|
|
|
:evaluated (zipmap evaluated-cells evaluated-values)}))
|
|
|
|
|
|
+(def parse-variables (memoize (fn [expression]
|
|
|
+ (js->clj (.getVariables mathjs expression)))))
|
|
|
+(defn temp3 [[k v]]
|
|
|
+ (let [w (name k)
|
|
|
+ c (re-find #"^[A-Z]+" w)
|
|
|
+ r (.parseInt js/window (re-find #"[0-9]+$" w))]
|
|
|
+ (do
|
|
|
+ (update-display c r v)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+
|
|
|
(defn temp2 [evaluated remaining]
|
|
|
(if (empty? remaining)
|
|
|
evaluated
|
|
|
- (let [jsvars (map #(.getVariables mathjs (val %)) remaining)
|
|
|
- vars (js->clj jsvars)
|
|
|
+ (let [vars (map #(parse-variables (val %)) remaining)
|
|
|
remaining-vars (zipmap (keys remaining) vars)
|
|
|
evaluated-vars (map (fn [x] (map #(let [item (keyword %)]
|
|
|
(cond
|
|
@@ -96,24 +86,60 @@
|
|
|
parsed (zipmap (keys remaining) evaluated-vars)
|
|
|
ready-or-not (group-by (fn [[ k v ]] (some #(not= :not-yet (last %)) v)) parsed)
|
|
|
;TODO: detect circular references
|
|
|
- ;prepared (map #([(key %) (.stringify js/JSON (apply js-obj (flatten (val %))))]) (ready-or-not true))
|
|
|
- ;prepared (map (fn [[k v]] [k (.evaluate mathjs (remaining k) (.stringify js/JSON (apply js-obj (flatten v)))) (.stringify js/JSON (apply js-obj (flatten v)))]) (ready-or-not true))
|
|
|
prepared (map (fn [[k v]] [k (.evaluate mathjs (remaining k) (apply js-obj (flatten v)))]) (ready-or-not true))
|
|
|
- ;TODO: merge new values into data with the :display keyword (or similar)
|
|
|
+ new-evaluated (reduce conj evaluated prepared)
|
|
|
+ still-remaining (apply hash-map (flatten (map #(list (key %) (remaining (key %))) (ready-or-not nil))))
|
|
|
+ ;merged (map temp3 prepared) ;TODO: only merge after recursion is complete (outside this function)
|
|
|
]
|
|
|
;remaining ; {:B8 B7 * 2, :C7 D1, :B7 C5 + D6}
|
|
|
+ ;vars ; ([B7] [D1] [C5 D6])
|
|
|
;remaining-vars ; {:B8 [B7], :C7 [D1], :B7 [C5 D6]}
|
|
|
;parsed ; {:B8 ([B7 :not-yet]), :C7 ([D1 0]), :B7 ([C5 269] [D6 4065])}
|
|
|
;ready-or-not ; {nil [[:B8 ([B7 :not-yet])]], true [[:C7 ([D1 0])] [:B7 ([C5 269] [D6 4065])]]}
|
|
|
- prepared ; ([:C7 0] [:B7 4334])
|
|
|
+ ;prepared ; ([:C7 0] [:B7 4334])
|
|
|
+ ;still-remaining ; {:B8 B7 * 2}
|
|
|
+ ;evaluated; TODO: change name, consider putting "data" directly in here
|
|
|
+ (recur new-evaluated still-remaining)
|
|
|
+ ;new-evaluated
|
|
|
)
|
|
|
)
|
|
|
)
|
|
|
|
|
|
+;; -------------------------
|
|
|
+;; Views
|
|
|
+
|
|
|
+(defn cell [c r data]
|
|
|
+ (let [datum (some #(if (and (= c (:col %)) (= r (:row %))) %) data)]
|
|
|
+ ^{:key (str c r)} [:td [:input {:value (if (= (get datum :view nil) :value) (get datum :value "") (get datum :display (get datum :value "")))
|
|
|
+ :on-change #(update-value c r datum (.. % -target -value))
|
|
|
+ :on-blur (fn [e] (println (str "blur! " c r)) (toggle-display c r :display))
|
|
|
+ :on-focus (fn [e] (println (str "focus! " c r)) (toggle-display c r :value))}]]
|
|
|
+ )
|
|
|
+ )
|
|
|
+(defn row [r cols data]
|
|
|
+ ^{:key (str "row-" r)} [:tr
|
|
|
+ (cons
|
|
|
+ ^{:key (str "row-head-" r)} [:th (str r)]
|
|
|
+ (map #(cell % r data) cols))])
|
|
|
+(defn header-row [cols]
|
|
|
+ ^{:key "header"} [:tr
|
|
|
+ (cons
|
|
|
+ ^{:key "corner"} [:th]
|
|
|
+ (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))])
|
|
|
+
|
|
|
+(defn sheet [data]
|
|
|
+ [:table [:tbody
|
|
|
+ (let [maxrow (highest :row data)
|
|
|
+ cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
|
|
|
+ (cons
|
|
|
+ (header-row cols)
|
|
|
+ (map #(row % cols data) (range 1 (inc maxrow)))))
|
|
|
+ ]])
|
|
|
+
|
|
|
(defn app []
|
|
|
[:div
|
|
|
- ;[:h3 "Microtables"]
|
|
|
- (do
|
|
|
+ [:h3 "Microtables"]
|
|
|
+ #_(do
|
|
|
(println (.stringify js/JSON (.parse mathjs "3 + 4x")))
|
|
|
(println (js->clj (.parse js/JSON (.stringify js/JSON (.parse mathjs "3 + 4x")))))
|
|
|
(println (type (js->clj (.stringify js/JSON (.parse mathjs "3 + 4x")))))
|
|
@@ -140,6 +166,8 @@
|
|
|
(do
|
|
|
(println rt)
|
|
|
(println (temp2 (:evaluated rt) (:remaining rt)))))
|
|
|
+ (println "data-atom")
|
|
|
+ (println @data-atom)
|
|
|
"hi")
|
|
|
[sheet @data-atom]])
|
|
|
|