Browse Source

feat: add ui for calculator mode in editor

Sebastian Bensusan 4 years ago
parent
commit
56de7fbd40

+ 32 - 0
src/main/frontend/extensions/calc.cljc

@@ -2,6 +2,7 @@
   (:refer-clojure :exclude [eval])
   (:refer-clojure :exclude [eval])
   (:require #?(:clj [clojure.java.io :as io])
   (:require #?(:clj [clojure.java.io :as io])
             #?(:cljs [shadow.resource :as rc])
             #?(:cljs [shadow.resource :as rc])
+            #?(:cljs [rum.core :as rum])
             [clojure.string :as str]
             [clojure.string :as str]
             [clojure.edn :as edn]
             [clojure.edn :as edn]
             [clojure.test :as test :refer [deftest testing is are]]
             [clojure.test :as test :refer [deftest testing is are]]
@@ -9,6 +10,9 @@
             #?(:clj [instaparse.core :as insta]
             #?(:clj [instaparse.core :as insta]
                :cljs [instaparse.core :as insta :refer-macros [defparser]])))
                :cljs [instaparse.core :as insta :refer-macros [defparser]])))
 
 
+;; ======================================================================
+;; Interpreter
+
 #?(:clj (def parse (insta/parser (io/resource "grammar/calc.bnf")))
 #?(:clj (def parse (insta/parser (io/resource "grammar/calc.bnf")))
    :cljs (defparser parse (rc/inline "grammar/calc.bnf")))
    :cljs (defparser parse (rc/inline "grammar/calc.bnf")))
 
 
@@ -53,3 +57,31 @@
                           (throw (ex-info (util/format "Can't find variable %s" var)
                           (throw (ex-info (util/format "Can't find variable %s" var)
                                           {:var var})))))}
                                           {:var var})))))}
      ast))))
      ast))))
+
+#?(:cljs
+   (defn eval-lines [s]
+     {:pre [(string? s)]}
+     (let [env (new-env)]
+       (->> (str/split-lines s)
+            (mapv (fn [line]
+                    (try
+                      (first (eval env (parse line)))
+                      (catch js/Error e
+                        nil))))))))
+
+;; ======================================================================
+;; UI
+
+#?(:cljs
+   (rum/defc results < rum/reactive
+     [calc-atom]
+     (when-let [output-lines (rum/react calc-atom)]
+       ;; the editor's parent will go into edit mode if any elements are clicked
+       ;; if we stop click propagation on this element, we allow the user to
+       ;; copy and paste the calc results
+       [:div.extensions__code-calc {:on-mouse-down (fn [e]
+                                                     (.stopPropagation e))}
+        ;; TODO: add react keys
+        (for [[i line] (map-indexed vector output-lines)]
+          [:div.extensions__code-calc-output-line {:key i}
+           [:span (or line "?")]])])))

+ 16 - 7
src/main/frontend/extensions/code.cljs

@@ -4,6 +4,7 @@
             [goog.dom :as gdom]
             [goog.dom :as gdom]
             [goog.object :as gobj]
             [goog.object :as gobj]
             [frontend.db :as db]
             [frontend.db :as db]
+            [frontend.extensions.calc :as calc]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.file :as file-handler]
@@ -136,6 +137,10 @@
                                                                (js/setTimeout #(reset! esc-pressed? false) 10))}}))]
                                                                (js/setTimeout #(reset! esc-pressed? false) 10))}}))]
       (when editor
       (when editor
         (let [element (.getWrapperElement editor)]
         (let [element (.getWrapperElement editor)]
+          (when (= mode "calc")
+            (.on editor "change" (fn [_cm e]
+                                   (let [new-code (.getValue editor)]
+                                     (reset! (:calc-atom state) (calc/eval-lines new-code))))))
           (.on editor "blur" (fn [_cm e]
           (.on editor "blur" (fn [_cm e]
                                (when e (util/stop e))
                                (when e (util/stop e))
                                (state/set-block-component-editing-mode! false)
                                (state/set-block-component-editing-mode! false)
@@ -158,7 +163,8 @@
 
 
 (rum/defcs editor < rum/reactive
 (rum/defcs editor < rum/reactive
   {:init (fn [state]
   {:init (fn [state]
-           (assoc state :editor-atom (atom nil)))
+           (let [[_ _ _ code _] (:rum/args state)]
+             (assoc state :editor-atom (atom nil) :calc-atom (atom (calc/eval-lines code)))))
    :did-mount (fn [state]
    :did-mount (fn [state]
                 (load-and-render! state)
                 (load-and-render! state)
                 state)
                 state)
@@ -171,10 +177,13 @@
   [state config id attr code theme options]
   [state config id attr code theme options]
   [:div.extensions__code
   [:div.extensions__code
    (when-let [mode (:data-lang attr)]
    (when-let [mode (:data-lang attr)]
-     [:div.extensions__code-lang
-      (let [mode (string/lower-case mode)]
-        (if (= mode "text/x-clojure")
-          "clojure"
-          mode))])
+     (when-not (= mode "calc")
+       [:div.extensions__code-lang
+        (let [mode (string/lower-case mode)]
+          (if (= mode "text/x-clojure")
+            "clojure"
+            mode))]))
    [:textarea (merge {:id id
    [:textarea (merge {:id id
-                      :default-value code} attr)]])
+                      :default-value code} attr)]
+   (when (= (:data-lang attr) "calc")
+     (calc/results (:calc-atom state)))])

+ 21 - 0
src/main/frontend/extensions/code.css

@@ -9,6 +9,27 @@
     background: var(--ls-secondary-background-color);
     background: var(--ls-secondary-background-color);
   }
   }
 
 
+  &-calc {
+    @apply absolute right-0 text-sm;
+    padding: 0 0.25em;
+    top: 3px;
+    z-index: 1;
+    background: transparent;
+    width: max-content;
+    text-align: right;
+
+    &-output-line {
+      height: 23px;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-around;
+
+      > span {
+          font-family: Fira Code, Monaco, Menlo, Consolas, 'COURIER NEW', monospace;
+      }
+    }
+  }
+
   > .CodeMirror {
   > .CodeMirror {
     z-index: 0;
     z-index: 0;
     height: auto;
     height: auto;