Commit 4c08b886 authored by Joachim Schunk's avatar Joachim Schunk

modify units-builder to use Bulma & identify units

parent 872158a2
Pipeline #45388 passed with stages
in 1 minute and 35 seconds
......@@ -2,6 +2,7 @@
(:require [garden.def :refer [defstyles]]
[garden.selectors :as selectors]))
(def color-info "#3298DC")
(def color-blue "#2196F3")
(def color-green "#4CAF50")
(def color-grey "#9E9E9E")
......@@ -15,9 +16,16 @@
[:html {:line-height 1.2}]
[:input {:color "inherit"}]
;; [:.shifttotop {:margin-top "-1rem"}]
[:.expo-container {:display "grid"
:grid-template-columns "14.0rem 3.0rem"
:grid-column-gap "0.3em"}
[:.value-builder-container {:display "grid"
:grid-template-columns "14.0rem 3.0rem"
:grid-column-gap "0.3em"}
[:.base {:grid-column "1 / 2"
:grid-row "2 / 4"}]
[:.expo {:grid-column "2 / 3"
:grid-row "1 / 3"}]]
[:.unit-builder-container {:display "grid"
:grid-template-columns "9.0rem 3.0rem"
:grid-column-gap "0.3em"}
[:.base {:grid-column "1 / 2"
:grid-row "2 / 4"}]
[:.expo {:grid-column "2 / 3"
......@@ -181,13 +189,11 @@
[:div.var-select {:cursor "pointer"}
[:&:hover {:color color-blue}]]
[:div.selectable-used-unit {:cursor "pointer"
:margin "1px"
:padding "4px"
:border-style "solid"
:border-width "1px"
:border-radius "4px"}
[:&:hover {:border-width "2px"
:border-color "green"}]]
:border-radius "2px"
:border-color color-info}
[:&:hover {:background-color color-info}]]
[:.tooltip
{:padding "10px 8px"
:font-size "1rem"
......
......@@ -21,15 +21,17 @@
[answer-scheme-version migrate-answer]]
[lernmeister.components.helper :refer [math-abs]])]))
(def prefix-mapping {"Y" 24
"Z" 21
"E" 18
"P" 15
"T" 12
"G" 9
"M" 6
"k" 3
"h" 2
(def prefix-mapping {"Y" 24
"Z" 21
"E" 18
"P" 15
"T" 12
"G" 9
"M" 6
"k" 3
"h" 2
"da" 1
"" 0
"d" -1
"c" -2
"m" -3
......@@ -39,9 +41,9 @@
"f" -15
"a" -18
"z" -21
"y" -24
"" 0
nil 0})
"y" -24})
(def prefix-set (set (keys prefix-mapping)))
(def si-unit-mapping {"m" [{:factor 1 :oom 0 :unit "m" :expo 1}]
"g" [{:factor 1 :oom -3 :unit "kg" :expo 1}]
......@@ -108,27 +110,33 @@
"d" [{:factor 8.64 :oom 4 :unit "s" :expo 1}]
"l" [{:factor 1 :oom -1 :unit "m" :expo 3}]})
(def additional-unit-mappings {"B" [{:factor 1 :oom 1 :unit "dB" :expo 1}]
(def additional-unit-mappings {"B" [{:factor 1 :oom 0 :unit "B" :expo 1}]
"°" [{:factor 1 :oom 0 :unit "°" :expo 1}]})
(def base-unit-set #{"m" "kg" "s" "A" "K" "mol" "cd" "dB" "°"})
(def unit-replace-map {"kg" {:unit "g" :prefix "k"}
"dB" {:unit "B" :prefix "d"}})
(def number-obj {:significand nil :order-of-magnitude "0"})
(def unit-mapping (merge si-unit-mapping additional-unit-mappings))
(def regex-split-char "§§")
(def unit-set (set (keys unit-mapping)))
(def unit-replace-set (set (keys unit-replace-map)))
(def base-unit-set #{"m" "kg" "s" "A" "K" "mol" "cd" "B" "°"})
(def base-unit-map (reduce (fn [res-map base-unit] (assoc res-map base-unit 0)) {} base-unit-set))
(def unit-mapping (merge si-unit-mapping additional-unit-mappings))
(def p-u-c (delay (reduce (fn [res m]
(let [k (first m)
v (second m)]
(if (contains? res k)
(assoc res k (conj (get res k) v))
(assoc res k [v]))))
{} (for [prefix prefix-set
unit unit-set]
[(str prefix unit) {:prefix prefix
:unit unit}]))))
(def prefix-set (set (keys prefix-mapping)))
(def regex-split-char "§§")
(def unit-set (set (keys unit-mapping)))
(def number-obj {:significand nil :order-of-magnitude "0"})
(defn prefix-unit-combinations [] (force p-u-c))
(defn add-to-baseunits-map [outer-multiplier result base-unit]
(let [multiplier (* outer-multiplier (:expo base-unit))
......
......@@ -3,11 +3,7 @@
[clojure.string :refer [join replace]]
[lernmeister.components.exercise-types.multistep-calculation.check
:refer
[prefix-set
unit-replace-map
unit-replace-set
unit-set
valid-significand?]]
[prefix-unit-combinations valid-significand?]]
[lernmeister.components.helper :refer [vec-remove]]))
(defn update-units [{:keys [units prefix unit expo]}]
......@@ -16,52 +12,40 @@
(vec-remove units-new index)
units-new)))
(defn check-and-replace-expo [unit-map]
(defn replace-expo [unit-map]
(let [expo (:expo unit-map)]
(when (not (#{"-" "0" "-0"} expo))
(if (contains? #{nil ""} expo) (assoc unit-map :expo "1") unit-map))))
(defn check-and-replace-unit [unit-map]
(when (not (and (unit-replace-set (:unit unit-map)) (not-empty (:prefix unit-map))))
(let [replaced-map (if-let [unit-mapping (get unit-replace-map (:unit unit-map))]
(-> unit-map
(assoc :unit (:unit unit-mapping))
(assoc :prefix (:prefix unit-mapping)))
unit-map)]
(when (unit-set (:unit replaced-map)) replaced-map))))
(defn check-and-replace-prefix [unit-map]
(let [replaced-map (assoc unit-map :prefix (or (:prefix unit-map) ""))]
(when (prefix-set (:prefix replaced-map)) replaced-map)))
(defn check-unit [unit-map]
(if (contains? #{nil "" "-"} expo)
(if (= "-" expo)
(assoc unit-map :expo "-1")
(assoc unit-map :expo "1"))
unit-map)))
(defn resolve-unit [{:keys [unit expo]}]
(when-let [possible-prefix-unit-combinations (get (prefix-unit-combinations) unit)]
(for [m possible-prefix-unit-combinations]
(assoc m :expo expo))))
(defn check-and-resolve-unit [unit-map]
(some-> unit-map
check-and-replace-expo
check-and-replace-unit
check-and-replace-prefix))
replace-expo
resolve-unit))
(defn parse-unit [string]
(when (string? string)
(when-let [matched-string (re-matches #"^[a-zA-ZΩ°]*" string)]
(replace (subs matched-string 0 3) #"Ohm|ohm" "Ω"))))
(defn parse-prefix [string]
(when (string? string)
(when-let [replaced-string (replace string "u" "µ")]
(let [last-char (or (last replaced-string) "")]
(when (contains? prefix-set last-char) last-char)))))
(when-let [matched-string (re-matches #"^[µa-zA-ZΩ°]*" string)]
(replace matched-string #"Ohm|ohm" "Ω"))))
(defn parse-int [string]
(when (string? string)
(when-let [[match sign int-part] (re-matches #"^(\-?)(\d*)$" string)]
(if-let [[match stripped-int-part] (re-matches #"^0*(\d+)$" int-part)]
(when-let [[_ sign int-part] (re-matches #"^(\-?)(\d*)$" string)]
(if-let [[_ stripped-int-part] (re-matches #"^0*(\d+)$" int-part)]
(str sign stripped-int-part)
(str sign)))))
(defn parse-float [string]
(when (string? string)
(when-let [[match sign int-part float-part] (re-matches #"^(\-?)(\d*)([\.|,]?\d*)$" string)]
(if-let [[match stripped-int-part] (re-matches #"^0*(\d+)$" int-part)]
(when-let [[_ sign int-part float-part] (re-matches #"^(\-?)(\d*)([\.|,]?\d*)$" string)]
(if-let [[_ stripped-int-part] (re-matches #"^0*(\d+)$" int-part)]
(str sign stripped-int-part (replace float-part "," "."))
(if (= float-part "") sign (str sign "0" (replace float-part "," ".")))))))
......
(ns lernmeister.components.modal-editor.builders
(:require [clojure.string :refer [replace]]
[lernmeister.components.common :refer [latex-span]]
[lernmeister.components.common :refer [info-line latex-span]]
[lernmeister.components.exercise-types.calchelper
:refer
[build-latex-string
build-units-latex-string
check-unit
check-and-resolve-unit
parse-float
parse-int
parse-prefix
parse-unit
update-units]]
[lernmeister.components.exercise-types.multistep-calculation.check
:refer
[unit-set]]
:refer [prefix-mapping]]
[lernmeister.components.helper :refer [vec-remove]]
[lernmeister.components.modal-editor.helper
:refer
......@@ -46,15 +44,25 @@
{} vec))
(defn selectable-used-unit [{:keys [u-map on-click-fn]}]
[:div.selectable-used-unit {:on-click #(on-click-fn (:units u-map))}
[latex-span (:latex-str u-map)]])
[:div.column.is-full.is-size-7.mb-1.has-text-centered {:on-click #(on-click-fn (:units u-map))}
[:div.selectable-used-unit.px-3.py-1 [latex-span (:latex-str u-map)]]])
(defn unit-chip [{:keys [prefix unit expo del-fn]}]
[:div.col.unit-chip.tag
(str prefix unit)
(when-not (= "1" expo) [:sup expo])
[:a {:on-click del-fn}
[:span.icon [:i.fas.fa-times]]]])
[:a.button.is-small.is-outlined.is-info {:on-click del-fn}
[:span.has-text-black (str prefix "\u200a" unit)
(when-not (= "1" expo) [:sup expo])]
[:span.icon.is-small [:i.fas.fa-times]]])
(defn unit-with-explanation [{:keys [prefix unit expo add-fn]}]
[:div.column.is-full.mt-1
[:div.buttons.has-addons
[:a.button.is-info {:on-click add-fn}
[:span.icon [:i.fas.fa-plus]]]
[:a.button.is-static
[:div [:span (str prefix \u200a unit) [:sup expo] " "]
(when (not-empty prefix)
(let [oom (get prefix-mapping prefix)]
[:span (str "(Einheit: " unit ", Präfix: " prefix " \u2259 10") [:sup oom] ")"]))]]]])
(defn category-var-selects [{:keys [category-selects idx var-ids path change-fn collats-fn
vars var-type]}]
......@@ -112,11 +120,12 @@
[:div.column.is-full
[:h5.title.is-5 "Wert (wissenschaftliche Notation): "]]
[:div.column.is-full
[:div.expo-container
[:div.value-builder-container
[:div.base
[ui/field {:addons? true}
[ui/input-text
{:value german-significand
{:placeholder "Zahl eingeben"
:value german-significand
:input-class "has-text-right"
:expanded? true
:on-change (fn [val] (when-let [v (parse-float val)]
......@@ -126,96 +135,88 @@
[ui/button {:static? true} "\u22c5"]
[ui/button {:static? true} "10"]]]
[:div.expo
[ui/field
[ui/field {:help "Exponent"}
[ui/input-text {:value oom
:on-change (fn [val] (when-let [v (parse-int val)]
(if collats-fn
(change-fn (conj path num-key :order-of-magnitude) v (collats-fn))
(change-fn (conj path num-key :order-of-magnitude) v))))}]]]]]]))
:on-change (fn [val]
(when-let [v (parse-int val)]
(if collats-fn
(change-fn (conj path num-key :order-of-magnitude) v (collats-fn))
(change-fn (conj path num-key :order-of-magnitude) v))))}]]]]]]))
(defn units-builder [{:keys [units path change-fn collats-fn used-units]}]
(reagent/with-let [cur-unit (reagent/atom {:prefix nil :unit nil :expo nil})
unit-options (apply (into sorted-set) (conj unit-set "kg" "Ohm"))]
[:div.row
[:div.col.s10
[:div.row
[:div.col.s12.font-plus [:b "Einheiten: "]]
(when-not (empty? units)
(map-indexed
(fn [index unit-entry]
(let [prefix (:prefix unit-entry)
unit (:unit unit-entry)
expo (:expo unit-entry)]
^{:key index}
[unit-chip {:prefix prefix
:unit unit
:expo expo
:del-fn #(change-fn (conj path :units) (vec-remove units index) (collats-fn))}]))
units))
(when (:invalid @cur-unit)
[:div.col
[ui/icon "warning" :class "orange-text"]
[:span " Ungültige Einheit"]])]
[:div.row
[:div.col.s2.expo-padding
[ui/input-text {:value (:prefix @cur-unit)
:label "Präfix"
:field-class "inline"
:input-class "right-align"
:input-size 3
:on-change (fn [val] (when-let [v (parse-prefix val)] (swap! cur-unit assoc :prefix v)))}]]
[:div.col.s2.expo-padding
[ui/input-text {:value (:unit @cur-unit)
:label "Einheit"
:field-class "inline"
:autocomplete {:options (into {} (map #(vector % nil) unit-options))}
:input-class "center-align"
:input-size 4
:on-change (fn [val] (when-let [v (parse-unit val)] (swap! cur-unit assoc :unit v)))}]]
[:div.col.s2
[ui/input-text {:value (:expo @cur-unit)
:label "Exponent"
:field-class "inline"
:input-class "left-align"
:input-size 2
:on-change (fn [val] (when-let [v (parse-int val)] (swap! cur-unit assoc :expo v)))}]]
[:div.col.s2.expo-padding
[:a.button.is-info
{:on-click (fn []
(if-let [checked-unit (check-unit @cur-unit)]
(do
(change-fn
(conj path :units) (update-units (assoc checked-unit :units units)) (collats-fn))
(reset! cur-unit {:prefix nil :unit nil :expo nil}))
(if (some
#((complement empty?) (val %))
(select-keys @cur-unit [:prefix :unit :expo]))
(swap! cur-unit assoc :invalid :unit)
(swap! cur-unit dissoc :invalid))))}
[:span.icon [:i.fas.fa-plus]]]]]
[:div.row
[:div.col.s10
[:span.icon [:i.far.fa-question-circle]]
"Bei zusammengesetzten Einheiten jeden Bestandtteil einzeln eingeben und mit + bestätigen."]
[:div.col.s12
[:span.icon [:i.far.fa-question-circle]]
[:span
"Brüche können mit negativem Exponent beschrieben werden." [:br]]]
[:div.col.s12
[:span.icon [:i.far.fa-question-circle]]
[:span "Beispiel: Eingabe \"Kilometer pro Quadratstunde\":"] [:br]
[:video.responsive-video {:controls true}
[:source {:src videos/unit-input-demo
:type "video/webm"}]]]
[:div.col.s12
[:span.icon [:i.far.fa-question-circle]]
[:span "Aufbau einer Einheit:"] [:br]
[:img {:src images/unit-description}]]]]
[:div.col.s2
(when (not-empty used-units)
[:<>
[:b "Direkt wählbar:"]
[:div.center-align
(reagent/with-let [cur-unit (reagent/atom {:unit nil :expo nil})]
[:<>
[:div.columns
[:div.column.is-three-quarters
[:div.columns.is-multiline.is-gapless
[:div.column.is-full
[:h5.title.is-5 "Einheiten: "]]
[:div.column.is-full.mb-4
(if-not (empty? units)
[:div.buttons
(map-indexed
(fn [index unit-entry]
(let [prefix (:prefix unit-entry)
unit (:unit unit-entry)
expo (:expo unit-entry)]
^{:key index}
[unit-chip {:prefix prefix
:unit unit
:expo expo
:del-fn #(change-fn (conj path :units) (vec-remove units index) (collats-fn))}]))
units)]
[:div.has-text-grey "Keine Einheit angegeben ..."])]
[:div.column.is-full
[:h5.subtitle.is-6 "Einheit hinzufügen:"]]
[:div.column.is-full
[:div.unit-builder-container
[:div.base
[ui/field
[ui/input-text {:placeholder "Einheit eingeben"
:value (:unit @cur-unit)
:input-class "has-text-right"
:on-change (fn [val]
(when-let [v (parse-unit val)] (swap! cur-unit assoc :unit v)))}]]]
[:div.expo
[ui/field {:help "Exponent"}
[ui/input-text {:placeholder "1"
:value (:expo @cur-unit)
:on-change (fn [val]
(when-let [v (parse-int (subs val 0 3))]
(when (not (#{"0" "-0"} v)) (swap! cur-unit assoc :expo v))))}]]]]]
(if-let [possible-units (check-and-resolve-unit @cur-unit)]
(map (fn [resolved-unit]
(let [add-fn #(do (change-fn
(conj path :units) (update-units (assoc resolved-unit :units units))
(collats-fn))
(reset! cur-unit {:unit nil :expo nil}))]
^{:key (str "add-unit-" (:unit resolved-unit))}
[unit-with-explanation (assoc resolved-unit :add-fn add-fn)]))
possible-units)
[:div.column.is-full.mt-1
[:div.buttons.has-addons
[:a.button {:disabled true} [:span.icon [:i.fas.fa-plus]]]
[:a.button.is-static "Keine gültige Einheit eingegeben"]]])
[:div.column.is-full.mt-5]
[info-line {:text "Mehrteilige Einheiten aus einzelnen Einheiten zusammensetzen."}]
[info-line {:text "Brüche durch Einheiten mit negativem Exponenten erzeugen."}]
[info-line {:text "Exponenten '1' und '-1' werden für Einheiten automatisch ergänzt"}]
;; [:div.col.s12
;; [:span.icon [:i.far.fa-question-circle]]
;; [:span "Beispiel: Eingabe \"Kilometer pro Quadratstunde\":"] [:br]
;; [:video.responsive-video {:controls true}
;; [:source {:src videos/unit-input-demo
;; :type "video/webm"}]]]
;; [:div.col.s12
;; [:span.icon [:i.far.fa-question-circle]]
;; [:span "Aufbau einer Einheit:"] [:br]
;; [:img {:src images/unit-description}]]
]]
[:div.column.is-one-quarter
(when (not-empty used-units)
[:div.columns.is-multiline.is-gapless
[:div.column.is-full
[:h5.subtitle.is-6 "Direkt wählbar:"]]
(map-indexed (fn [index item]
^{:key (str "used-units-" index)}
[selectable-used-unit
......@@ -223,7 +224,7 @@
:on-click-fn (fn [u]
(change-fn (conj path :units) u)
(reset! cur-unit {:prefix nil :unit nil :expo nil}))}])
(sort-by (comp count key) > used-units))]])]]))
(sort-by (comp count key) > used-units))])]]]))
(defn var-mapping-builder [{:keys [steps assocs params assignments path change-fn phrase-fn with-oom
collats-fn var-mapping var-set var-phys-val-map calc-mapping]}]
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment