core.cljs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. (ns microtables-frontend.core
  2. (:require
  3. [reagent.core :as r]
  4. ["mathjs" :as mathjs]))
  5. ; to generate random values
  6. ;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)}"}`); }
  7. (def sample-data [{:row 1 :col "A" :value "59"}
  8. {:row 5 :col "C" :value "269"}
  9. {:row 4 :col "B" :value "7893"}
  10. {:row 2 :col "F" :value "8650"}
  11. {:row 6 :col "D" :value "4065"}
  12. {:row 7 :col "F" :value "5316"}
  13. {:row 12 :col "A" :value "2405"}
  14. {:row 5 :col "B" :value "7863"}
  15. {:row 9 :col "E" :value "3144"}
  16. {:row 10 :col "D" :value "8272"}
  17. {:row 11 :col "D" :value "2495"}
  18. {:row 15 :col "E" :value "8968"}])
  19. (defonce data-atom (r/atom sample-data))
  20. (defn highest [dir data] (apply max (map dir data)))
  21. ; COLUMN NAMES
  22. (defn upgrade-letter-code [s]
  23. (let [l (last s)]
  24. (cond
  25. (empty? s) [65]
  26. (= l 90) (conj (upgrade-letter-code (subvec s 0 (dec (count s)))) 65)
  27. :else (conj (subvec s 0 (dec (count s))) (inc l)))))
  28. (defn next-letter [lc]
  29. (apply str (map char (upgrade-letter-code (mapv #(.charCodeAt % 0) lc)))))
  30. (def col-letters (iterate next-letter "A"))
  31. ; CHANGE VALUE FUNCTIONS
  32. (defn update-value [c r datum value]
  33. (if (nil? datum)
  34. (swap! data-atom conj {:row r :col c :value value})
  35. (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :value value) %) d)))))
  36. ;; -------------------------
  37. ;; Views
  38. (defn cell [c r data]
  39. (let [datum (some #(if (and (= c (:col %)) (= r (:row %))) %) data)]
  40. ^{:key (str c r)} [:td [:input {:value (or (:value datum) "")
  41. :on-change #(update-value c r datum (.. % -target -value))
  42. :on-blur (fn [e] (println (str "blur! " c r)))
  43. :on-focus (fn [e] (println (str "focus! " c r)))}]]
  44. )
  45. )
  46. (defn row [r cols data]
  47. ^{:key (str "row-" r)} [:tr
  48. (cons
  49. ^{:key (str "row-head-" r)} [:th (str r)]
  50. (map #(cell % r data) cols))])
  51. (defn header-row [cols]
  52. ^{:key "header"} [:tr
  53. (cons
  54. ^{:key "corner"} [:th]
  55. (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))])
  56. (defn sheet [data]
  57. [:table [:tbody
  58. (let [maxrow (highest :row data)
  59. cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
  60. (cons
  61. (header-row cols)
  62. (map #(row % cols data) (range 1 (inc maxrow)))))
  63. ]])
  64. (defn temp1 [data]
  65. (let [first-order (group-by #(not= (first (:value %)) "=") data)
  66. evaluated-cells (map #(keyword (str (:col %) (:row %))) (first-order true))
  67. non-evaluated-cells (map #(keyword (str (:col %) (:row %))) (first-order false))
  68. evaluated-values (map :value (first-order true))
  69. non-evaluated-values (map #(subs (:value %) 1) (first-order false))]
  70. {:remaining (zipmap non-evaluated-cells non-evaluated-values)
  71. :evaluated (zipmap evaluated-cells evaluated-values)}))
  72. (defn temp2 [evaluated remaining]
  73. (if (empty? remaining)
  74. evaluated
  75. (let [jsvars (map #(.getVariables mathjs (val %)) remaining)
  76. vars (js->clj jsvars)
  77. remaining-vars (zipmap (keys remaining) vars)
  78. evaluated-vars (map (fn [x] (map #(let [item (keyword %)]
  79. (cond
  80. (contains? evaluated item) [% (evaluated item)]
  81. (contains? remaining item) [% :not-yet]
  82. :else [% 0]
  83. )) x)) vars)
  84. parsed (zipmap (keys remaining) evaluated-vars)
  85. ready-or-not (group-by (fn [[ k v ]] (some #(not= :not-yet (last %)) v)) parsed)
  86. ;TODO: detect circular references
  87. ;prepared (map #([(key %) (.stringify js/JSON (apply js-obj (flatten (val %))))]) (ready-or-not true))
  88. ;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))
  89. prepared (map (fn [[k v]] [k (.evaluate mathjs (remaining k) (apply js-obj (flatten v)))]) (ready-or-not true))
  90. ;TODO: merge new values into data with the :display keyword (or similar)
  91. ]
  92. ;remaining ; {:B8 B7 * 2, :C7 D1, :B7 C5 + D6}
  93. ;remaining-vars ; {:B8 [B7], :C7 [D1], :B7 [C5 D6]}
  94. ;parsed ; {:B8 ([B7 :not-yet]), :C7 ([D1 0]), :B7 ([C5 269] [D6 4065])}
  95. ;ready-or-not ; {nil [[:B8 ([B7 :not-yet])]], true [[:C7 ([D1 0])] [:B7 ([C5 269] [D6 4065])]]}
  96. prepared ; ([:C7 0] [:B7 4334])
  97. )
  98. )
  99. )
  100. (defn app []
  101. [:div
  102. ;[:h3 "Microtables"]
  103. (do
  104. (println (.stringify js/JSON (.parse mathjs "3 + 4x")))
  105. (println (js->clj (.parse js/JSON (.stringify js/JSON (.parse mathjs "3 + 4x")))))
  106. (println (type (js->clj (.stringify js/JSON (.parse mathjs "3 + 4x")))))
  107. (println (js-obj "a" 2 "b" "one"))
  108. (println (type (js-obj "a" 2 "b" "one")))
  109. (println (type (.parse mathjs "3 + 4x")))
  110. (println (.log js/console (.parse mathjs "3 + 4x")))
  111. (println (.keys js/Object (.parse mathjs "3 + 4x")))
  112. (println (js->clj (.parse mathjs "3 + 4x")))
  113. (println (js->clj (.parse mathjs "3 + 4x") :keywordize-keys true))
  114. (println (.-op (.parse mathjs "3 + 4x")))
  115. (println (.-fn (.parse mathjs "3 + 4x")))
  116. (println (js->clj (.-args (.parse mathjs "3 + 4x"))))
  117. (println (.-comment (.parse mathjs "3 + 4x")))
  118. (println (.-implicit (.parse mathjs "3 + 4x")))
  119. (println (.-value (.parse mathjs "3 + 4x")))
  120. (println (second (.-args (.parse mathjs "3 + 4x"))))
  121. (println (.-implicit (second (.-args (.parse mathjs "3 + 4x")))))
  122. (println (.-op (second (.-args (.parse mathjs "3 + 4x")))))
  123. (println (.-fn (second (.-args (.parse mathjs "3 + 4x")))))
  124. (println (.evaluate mathjs "3 + 4x" (js-obj "x" 10)))
  125. (println (filter #(not= (first (:value %)) "=") @data-atom))
  126. (let [rt (temp1 @data-atom)]
  127. (do
  128. (println rt)
  129. (println (temp2 (:evaluated rt) (:remaining rt)))))
  130. "hi")
  131. [sheet @data-atom]])
  132. ;; -------------------------
  133. ;; Initialize app
  134. (defn mount-root []
  135. (r/render [app] (.getElementById js/document "app")))
  136. (defn init! []
  137. (mount-root))